annotate web/bottle.py @ 620:8fda564cc46f rust

fridge work
author Matt Johnston <matt@ucc.asn.au>
date Thu, 23 Mar 2017 00:20:22 +0800
parents 317a1738f15c
children
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1 #!/usr/bin/env python
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2 # -*- coding: utf-8 -*-
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3 """
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
4 Bottle is a fast and simple micro-framework for small web applications. It
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
5 offers request dispatching (Routes) with url parameter support, templates,
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
6 a built-in HTTP Server and adapters for many third party WSGI/HTTP-server and
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
7 template engines - all in a single file and with no dependencies other than the
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
8 Python Standard Library.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
9
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
10 Homepage and documentation: http://bottlepy.org/
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
11
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
12 Copyright (c) 2013, Marcel Hellkamp.
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
13 License: MIT (see LICENSE for details)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
14 """
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
15
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
16 from __future__ import with_statement
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
17
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
18 __author__ = 'Marcel Hellkamp'
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
19 __version__ = '0.12.8'
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
20 __license__ = 'MIT'
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
21
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
22 # The gevent server adapter needs to patch some modules before they are imported
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
23 # This is why we parse the commandline parameters here but handle them later
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
24 if __name__ == '__main__':
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
25 from optparse import OptionParser
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
26 _cmd_parser = OptionParser(usage="usage: %prog [options] package.module:app")
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
27 _opt = _cmd_parser.add_option
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
28 _opt("--version", action="store_true", help="show version number.")
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
29 _opt("-b", "--bind", metavar="ADDRESS", help="bind socket to ADDRESS.")
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
30 _opt("-s", "--server", default='wsgiref', help="use SERVER as backend.")
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
31 _opt("-p", "--plugin", action="append", help="install additional plugin/s.")
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
32 _opt("--debug", action="store_true", help="start server in debug mode.")
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
33 _opt("--reload", action="store_true", help="auto-reload on file changes.")
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
34 _cmd_options, _cmd_args = _cmd_parser.parse_args()
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
35 if _cmd_options.server and _cmd_options.server.startswith('gevent'):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
36 import gevent.monkey; gevent.monkey.patch_all()
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
37
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
38 import base64, cgi, email.utils, functools, hmac, imp, itertools, mimetypes,\
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
39 os, re, subprocess, sys, tempfile, threading, time, warnings
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
40
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
41 from datetime import date as datedate, datetime, timedelta
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
42 from tempfile import TemporaryFile
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
43 from traceback import format_exc, print_exc
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
44 from inspect import getargspec
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
45 from unicodedata import normalize
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
46
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
47
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
48 try: from simplejson import dumps as json_dumps, loads as json_lds
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
49 except ImportError: # pragma: no cover
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
50 try: from json import dumps as json_dumps, loads as json_lds
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
51 except ImportError:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
52 try: from django.utils.simplejson import dumps as json_dumps, loads as json_lds
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
53 except ImportError:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
54 def json_dumps(data):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
55 raise ImportError("JSON support requires Python 2.6 or simplejson.")
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
56 json_lds = json_dumps
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
57
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
58
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
59
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
60 # We now try to fix 2.5/2.6/3.1/3.2 incompatibilities.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
61 # It ain't pretty but it works... Sorry for the mess.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
62
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
63 py = sys.version_info
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
64 py3k = py >= (3, 0, 0)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
65 py25 = py < (2, 6, 0)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
66 py31 = (3, 1, 0) <= py < (3, 2, 0)
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
67
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
68 # Workaround for the missing "as" keyword in py3k.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
69 def _e(): return sys.exc_info()[1]
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
70
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
71 # Workaround for the "print is a keyword/function" Python 2/3 dilemma
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
72 # and a fallback for mod_wsgi (resticts stdout/err attribute access)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
73 try:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
74 _stdout, _stderr = sys.stdout.write, sys.stderr.write
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
75 except IOError:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
76 _stdout = lambda x: sys.stdout.write(x)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
77 _stderr = lambda x: sys.stderr.write(x)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
78
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
79 # Lots of stdlib and builtin differences.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
80 if py3k:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
81 import http.client as httplib
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
82 import _thread as thread
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
83 from urllib.parse import urljoin, SplitResult as UrlSplitResult
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
84 from urllib.parse import urlencode, quote as urlquote, unquote as urlunquote
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
85 urlunquote = functools.partial(urlunquote, encoding='latin1')
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
86 from http.cookies import SimpleCookie
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
87 from collections import MutableMapping as DictMixin
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
88 import pickle
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
89 from io import BytesIO
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
90 from configparser import ConfigParser
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
91 basestring = str
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
92 unicode = str
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
93 json_loads = lambda s: json_lds(touni(s))
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
94 callable = lambda x: hasattr(x, '__call__')
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
95 imap = map
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
96 def _raise(*a): raise a[0](a[1]).with_traceback(a[2])
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
97 else: # 2.x
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
98 import httplib
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
99 import thread
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
100 from urlparse import urljoin, SplitResult as UrlSplitResult
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
101 from urllib import urlencode, quote as urlquote, unquote as urlunquote
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
102 from Cookie import SimpleCookie
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
103 from itertools import imap
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
104 import cPickle as pickle
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
105 from StringIO import StringIO as BytesIO
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
106 from ConfigParser import SafeConfigParser as ConfigParser
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
107 if py25:
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
108 msg = "Python 2.5 support may be dropped in future versions of Bottle."
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
109 warnings.warn(msg, DeprecationWarning)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
110 from UserDict import DictMixin
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
111 def next(it): return it.next()
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
112 bytes = str
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
113 else: # 2.6, 2.7
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
114 from collections import MutableMapping as DictMixin
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
115 unicode = unicode
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
116 json_loads = json_lds
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
117 eval(compile('def _raise(*a): raise a[0], a[1], a[2]', '<py3fix>', 'exec'))
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
118
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
119 # Some helpers for string/byte handling
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
120 def tob(s, enc='utf8'):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
121 return s.encode(enc) if isinstance(s, unicode) else bytes(s)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
122 def touni(s, enc='utf8', err='strict'):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
123 return s.decode(enc, err) if isinstance(s, bytes) else unicode(s)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
124 tonat = touni if py3k else tob
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
125
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
126 # 3.2 fixes cgi.FieldStorage to accept bytes (which makes a lot of sense).
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
127 # 3.1 needs a workaround.
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
128 if py31:
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
129 from io import TextIOWrapper
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
130 class NCTextIOWrapper(TextIOWrapper):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
131 def close(self): pass # Keep wrapped buffer open.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
132
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
133
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
134 # A bug in functools causes it to break if the wrapper is an instance method
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
135 def update_wrapper(wrapper, wrapped, *a, **ka):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
136 try: functools.update_wrapper(wrapper, wrapped, *a, **ka)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
137 except AttributeError: pass
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
138
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
139
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
140
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
141 # These helpers are used at module level and need to be defined first.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
142 # And yes, I know PEP-8, but sometimes a lower-case classname makes more sense.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
143
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
144 def depr(message, hard=False):
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
145 warnings.warn(message, DeprecationWarning, stacklevel=3)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
146
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
147 def makelist(data): # This is just to handy
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
148 if isinstance(data, (tuple, list, set, dict)): return list(data)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
149 elif data: return [data]
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
150 else: return []
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
151
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
152
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
153 class DictProperty(object):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
154 ''' Property that maps to a key in a local dict-like attribute. '''
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
155 def __init__(self, attr, key=None, read_only=False):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
156 self.attr, self.key, self.read_only = attr, key, read_only
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
157
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
158 def __call__(self, func):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
159 functools.update_wrapper(self, func, updated=[])
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
160 self.getter, self.key = func, self.key or func.__name__
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
161 return self
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
162
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
163 def __get__(self, obj, cls):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
164 if obj is None: return self
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
165 key, storage = self.key, getattr(obj, self.attr)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
166 if key not in storage: storage[key] = self.getter(obj)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
167 return storage[key]
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
168
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
169 def __set__(self, obj, value):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
170 if self.read_only: raise AttributeError("Read-Only property.")
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
171 getattr(obj, self.attr)[self.key] = value
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
172
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
173 def __delete__(self, obj):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
174 if self.read_only: raise AttributeError("Read-Only property.")
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
175 del getattr(obj, self.attr)[self.key]
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
176
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
177
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
178 class cached_property(object):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
179 ''' A property that is only computed once per instance and then replaces
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
180 itself with an ordinary attribute. Deleting the attribute resets the
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
181 property. '''
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
182
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
183 def __init__(self, func):
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
184 self.__doc__ = getattr(func, '__doc__')
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
185 self.func = func
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
186
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
187 def __get__(self, obj, cls):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
188 if obj is None: return self
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
189 value = obj.__dict__[self.func.__name__] = self.func(obj)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
190 return value
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
191
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
192
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
193 class lazy_attribute(object):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
194 ''' A property that caches itself to the class object. '''
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
195 def __init__(self, func):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
196 functools.update_wrapper(self, func, updated=[])
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
197 self.getter = func
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
198
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
199 def __get__(self, obj, cls):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
200 value = self.getter(cls)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
201 setattr(cls, self.__name__, value)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
202 return value
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
203
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
204
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
205
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
206
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
207
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
208
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
209 ###############################################################################
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
210 # Exceptions and Events ########################################################
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
211 ###############################################################################
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
212
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
213
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
214 class BottleException(Exception):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
215 """ A base class for exceptions used by bottle. """
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
216 pass
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
217
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
218
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
219
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
220
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
221
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
222
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
223 ###############################################################################
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
224 # Routing ######################################################################
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
225 ###############################################################################
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
226
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
227
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
228 class RouteError(BottleException):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
229 """ This is a base class for all routing related exceptions """
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
230
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
231
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
232 class RouteReset(BottleException):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
233 """ If raised by a plugin or request handler, the route is reset and all
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
234 plugins are re-applied. """
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
235
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
236 class RouterUnknownModeError(RouteError): pass
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
237
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
238
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
239 class RouteSyntaxError(RouteError):
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
240 """ The route parser found something not supported by this router. """
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
241
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
242
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
243 class RouteBuildError(RouteError):
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
244 """ The route could not be built. """
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
245
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
246
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
247 def _re_flatten(p):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
248 ''' Turn all capturing groups in a regular expression pattern into
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
249 non-capturing groups. '''
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
250 if '(' not in p: return p
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
251 return re.sub(r'(\\*)(\(\?P<[^>]+>|\((?!\?))',
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
252 lambda m: m.group(0) if len(m.group(1)) % 2 else m.group(1) + '(?:', p)
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
253
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
254
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
255 class Router(object):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
256 ''' A Router is an ordered collection of route->target pairs. It is used to
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
257 efficiently match WSGI requests against a number of routes and return
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
258 the first target that satisfies the request. The target may be anything,
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
259 usually a string, ID or callable object. A route consists of a path-rule
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
260 and a HTTP method.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
261
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
262 The path-rule is either a static path (e.g. `/contact`) or a dynamic
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
263 path that contains wildcards (e.g. `/wiki/<page>`). The wildcard syntax
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
264 and details on the matching order are described in docs:`routing`.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
265 '''
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
266
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
267 default_pattern = '[^/]+'
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
268 default_filter = 're'
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
269
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
270 #: The current CPython regexp implementation does not allow more
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
271 #: than 99 matching groups per regular expression.
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
272 _MAX_GROUPS_PER_PATTERN = 99
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
273
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
274 def __init__(self, strict=False):
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
275 self.rules = [] # All rules in order
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
276 self._groups = {} # index of regexes to find them in dyna_routes
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
277 self.builder = {} # Data structure for the url builder
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
278 self.static = {} # Search structure for static routes
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
279 self.dyna_routes = {}
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
280 self.dyna_regexes = {} # Search structure for dynamic routes
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
281 #: If true, static routes are no longer checked first.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
282 self.strict_order = strict
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
283 self.filters = {
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
284 're': lambda conf:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
285 (_re_flatten(conf or self.default_pattern), None, None),
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
286 'int': lambda conf: (r'-?\d+', int, lambda x: str(int(x))),
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
287 'float': lambda conf: (r'-?[\d.]+', float, lambda x: str(float(x))),
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
288 'path': lambda conf: (r'.+?', None, None)}
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
289
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
290 def add_filter(self, name, func):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
291 ''' Add a filter. The provided function is called with the configuration
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
292 string as parameter and must return a (regexp, to_python, to_url) tuple.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
293 The first element is a string, the last two are callables or None. '''
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
294 self.filters[name] = func
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
295
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
296 rule_syntax = re.compile('(\\\\*)'\
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
297 '(?:(?::([a-zA-Z_][a-zA-Z_0-9]*)?()(?:#(.*?)#)?)'\
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
298 '|(?:<([a-zA-Z_][a-zA-Z_0-9]*)?(?::([a-zA-Z_]*)'\
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
299 '(?::((?:\\\\.|[^\\\\>]+)+)?)?)?>))')
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
300
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
301 def _itertokens(self, rule):
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
302 offset, prefix = 0, ''
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
303 for match in self.rule_syntax.finditer(rule):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
304 prefix += rule[offset:match.start()]
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
305 g = match.groups()
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
306 if len(g[0])%2: # Escaped wildcard
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
307 prefix += match.group(0)[len(g[0]):]
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
308 offset = match.end()
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
309 continue
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
310 if prefix:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
311 yield prefix, None, None
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
312 name, filtr, conf = g[4:7] if g[2] is None else g[1:4]
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
313 yield name, filtr or 'default', conf or None
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
314 offset, prefix = match.end(), ''
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
315 if offset <= len(rule) or prefix:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
316 yield prefix+rule[offset:], None, None
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
317
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
318 def add(self, rule, method, target, name=None):
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
319 ''' Add a new rule or replace the target for an existing rule. '''
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
320 anons = 0 # Number of anonymous wildcards found
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
321 keys = [] # Names of keys
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
322 pattern = '' # Regular expression pattern with named groups
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
323 filters = [] # Lists of wildcard input filters
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
324 builder = [] # Data structure for the URL builder
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
325 is_static = True
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
326
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
327 for key, mode, conf in self._itertokens(rule):
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
328 if mode:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
329 is_static = False
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
330 if mode == 'default': mode = self.default_filter
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
331 mask, in_filter, out_filter = self.filters[mode](conf)
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
332 if not key:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
333 pattern += '(?:%s)' % mask
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
334 key = 'anon%d' % anons
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
335 anons += 1
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
336 else:
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
337 pattern += '(?P<%s>%s)' % (key, mask)
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
338 keys.append(key)
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
339 if in_filter: filters.append((key, in_filter))
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
340 builder.append((key, out_filter or str))
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
341 elif key:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
342 pattern += re.escape(key)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
343 builder.append((None, key))
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
344
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
345 self.builder[rule] = builder
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
346 if name: self.builder[name] = builder
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
347
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
348 if is_static and not self.strict_order:
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
349 self.static.setdefault(method, {})
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
350 self.static[method][self.build(rule)] = (target, None)
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
351 return
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
352
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
353 try:
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
354 re_pattern = re.compile('^(%s)$' % pattern)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
355 re_match = re_pattern.match
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
356 except re.error:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
357 raise RouteSyntaxError("Could not add Route: %s (%s)" % (rule, _e()))
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
358
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
359 if filters:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
360 def getargs(path):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
361 url_args = re_match(path).groupdict()
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
362 for name, wildcard_filter in filters:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
363 try:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
364 url_args[name] = wildcard_filter(url_args[name])
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
365 except ValueError:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
366 raise HTTPError(400, 'Path has wrong format.')
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
367 return url_args
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
368 elif re_pattern.groupindex:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
369 def getargs(path):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
370 return re_match(path).groupdict()
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
371 else:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
372 getargs = None
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
373
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
374 flatpat = _re_flatten(pattern)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
375 whole_rule = (rule, flatpat, target, getargs)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
376
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
377 if (flatpat, method) in self._groups:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
378 if DEBUG:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
379 msg = 'Route <%s %s> overwrites a previously defined route'
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
380 warnings.warn(msg % (method, rule), RuntimeWarning)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
381 self.dyna_routes[method][self._groups[flatpat, method]] = whole_rule
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
382 else:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
383 self.dyna_routes.setdefault(method, []).append(whole_rule)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
384 self._groups[flatpat, method] = len(self.dyna_routes[method]) - 1
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
385
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
386 self._compile(method)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
387
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
388 def _compile(self, method):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
389 all_rules = self.dyna_routes[method]
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
390 comborules = self.dyna_regexes[method] = []
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
391 maxgroups = self._MAX_GROUPS_PER_PATTERN
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
392 for x in range(0, len(all_rules), maxgroups):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
393 some = all_rules[x:x+maxgroups]
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
394 combined = (flatpat for (_, flatpat, _, _) in some)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
395 combined = '|'.join('(^%s$)' % flatpat for flatpat in combined)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
396 combined = re.compile(combined).match
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
397 rules = [(target, getargs) for (_, _, target, getargs) in some]
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
398 comborules.append((combined, rules))
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
399
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
400 def build(self, _name, *anons, **query):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
401 ''' Build an URL by filling the wildcards in a rule. '''
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
402 builder = self.builder.get(_name)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
403 if not builder: raise RouteBuildError("No route with that name.", _name)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
404 try:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
405 for i, value in enumerate(anons): query['anon%d'%i] = value
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
406 url = ''.join([f(query.pop(n)) if n else f for (n,f) in builder])
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
407 return url if not query else url+'?'+urlencode(query)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
408 except KeyError:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
409 raise RouteBuildError('Missing URL argument: %r' % _e().args[0])
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
410
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
411 def match(self, environ):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
412 ''' Return a (target, url_agrs) tuple or raise HTTPError(400/404/405). '''
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
413 verb = environ['REQUEST_METHOD'].upper()
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
414 path = environ['PATH_INFO'] or '/'
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
415 target = None
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
416 if verb == 'HEAD':
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
417 methods = ['PROXY', verb, 'GET', 'ANY']
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
418 else:
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
419 methods = ['PROXY', verb, 'ANY']
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
420
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
421 for method in methods:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
422 if method in self.static and path in self.static[method]:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
423 target, getargs = self.static[method][path]
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
424 return target, getargs(path) if getargs else {}
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
425 elif method in self.dyna_regexes:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
426 for combined, rules in self.dyna_regexes[method]:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
427 match = combined(path)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
428 if match:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
429 target, getargs = rules[match.lastindex - 1]
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
430 return target, getargs(path) if getargs else {}
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
431
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
432 # No matching route found. Collect alternative methods for 405 response
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
433 allowed = set([])
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
434 nocheck = set(methods)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
435 for method in set(self.static) - nocheck:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
436 if path in self.static[method]:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
437 allowed.add(verb)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
438 for method in set(self.dyna_regexes) - allowed - nocheck:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
439 for combined, rules in self.dyna_regexes[method]:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
440 match = combined(path)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
441 if match:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
442 allowed.add(method)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
443 if allowed:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
444 allow_header = ",".join(sorted(allowed))
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
445 raise HTTPError(405, "Method not allowed.", Allow=allow_header)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
446
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
447 # No matching route and no alternative method found. We give up
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
448 raise HTTPError(404, "Not found: " + repr(path))
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
449
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
450
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
451
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
452
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
453
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
454
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
455 class Route(object):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
456 ''' This class wraps a route callback along with route specific metadata and
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
457 configuration and applies Plugins on demand. It is also responsible for
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
458 turing an URL path rule into a regular expression usable by the Router.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
459 '''
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
460
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
461 def __init__(self, app, rule, method, callback, name=None,
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
462 plugins=None, skiplist=None, **config):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
463 #: The application this route is installed to.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
464 self.app = app
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
465 #: The path-rule string (e.g. ``/wiki/:page``).
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
466 self.rule = rule
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
467 #: The HTTP method as a string (e.g. ``GET``).
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
468 self.method = method
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
469 #: The original callback with no plugins applied. Useful for introspection.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
470 self.callback = callback
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
471 #: The name of the route (if specified) or ``None``.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
472 self.name = name or None
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
473 #: A list of route-specific plugins (see :meth:`Bottle.route`).
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
474 self.plugins = plugins or []
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
475 #: A list of plugins to not apply to this route (see :meth:`Bottle.route`).
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
476 self.skiplist = skiplist or []
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
477 #: Additional keyword arguments passed to the :meth:`Bottle.route`
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
478 #: decorator are stored in this dictionary. Used for route-specific
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
479 #: plugin configuration and meta-data.
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
480 self.config = ConfigDict().load_dict(config, make_namespaces=True)
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
481
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
482 def __call__(self, *a, **ka):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
483 depr("Some APIs changed to return Route() instances instead of"\
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
484 " callables. Make sure to use the Route.call method and not to"\
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
485 " call Route instances directly.") #0.12
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
486 return self.call(*a, **ka)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
487
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
488 @cached_property
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
489 def call(self):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
490 ''' The route callback with all plugins applied. This property is
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
491 created on demand and then cached to speed up subsequent requests.'''
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
492 return self._make_callback()
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
493
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
494 def reset(self):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
495 ''' Forget any cached values. The next time :attr:`call` is accessed,
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
496 all plugins are re-applied. '''
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
497 self.__dict__.pop('call', None)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
498
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
499 def prepare(self):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
500 ''' Do all on-demand work immediately (useful for debugging).'''
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
501 self.call
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
502
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
503 @property
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
504 def _context(self):
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
505 depr('Switch to Plugin API v2 and access the Route object directly.') #0.12
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
506 return dict(rule=self.rule, method=self.method, callback=self.callback,
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
507 name=self.name, app=self.app, config=self.config,
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
508 apply=self.plugins, skip=self.skiplist)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
509
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
510 def all_plugins(self):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
511 ''' Yield all Plugins affecting this route. '''
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
512 unique = set()
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
513 for p in reversed(self.app.plugins + self.plugins):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
514 if True in self.skiplist: break
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
515 name = getattr(p, 'name', False)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
516 if name and (name in self.skiplist or name in unique): continue
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
517 if p in self.skiplist or type(p) in self.skiplist: continue
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
518 if name: unique.add(name)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
519 yield p
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
520
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
521 def _make_callback(self):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
522 callback = self.callback
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
523 for plugin in self.all_plugins():
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
524 try:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
525 if hasattr(plugin, 'apply'):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
526 api = getattr(plugin, 'api', 1)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
527 context = self if api > 1 else self._context
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
528 callback = plugin.apply(callback, context)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
529 else:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
530 callback = plugin(callback)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
531 except RouteReset: # Try again with changed configuration.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
532 return self._make_callback()
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
533 if not callback is self.callback:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
534 update_wrapper(callback, self.callback)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
535 return callback
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
536
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
537 def get_undecorated_callback(self):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
538 ''' Return the callback. If the callback is a decorated function, try to
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
539 recover the original function. '''
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
540 func = self.callback
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
541 func = getattr(func, '__func__' if py3k else 'im_func', func)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
542 closure_attr = '__closure__' if py3k else 'func_closure'
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
543 while hasattr(func, closure_attr) and getattr(func, closure_attr):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
544 func = getattr(func, closure_attr)[0].cell_contents
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
545 return func
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
546
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
547 def get_callback_args(self):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
548 ''' Return a list of argument names the callback (most likely) accepts
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
549 as keyword arguments. If the callback is a decorated function, try
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
550 to recover the original function before inspection. '''
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
551 return getargspec(self.get_undecorated_callback())[0]
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
552
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
553 def get_config(self, key, default=None):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
554 ''' Lookup a config field and return its value, first checking the
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
555 route.config, then route.app.config.'''
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
556 for conf in (self.config, self.app.conifg):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
557 if key in conf: return conf[key]
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
558 return default
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
559
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
560 def __repr__(self):
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
561 cb = self.get_undecorated_callback()
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
562 return '<%s %r %r>' % (self.method, self.rule, cb)
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
563
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
564
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
565
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
566
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
567
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
568
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
569 ###############################################################################
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
570 # Application Object ###########################################################
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
571 ###############################################################################
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
572
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
573
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
574 class Bottle(object):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
575 """ Each Bottle object represents a single, distinct web application and
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
576 consists of routes, callbacks, plugins, resources and configuration.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
577 Instances are callable WSGI applications.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
578
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
579 :param catchall: If true (default), handle all exceptions. Turn off to
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
580 let debugging middleware handle exceptions.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
581 """
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
582
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
583 def __init__(self, catchall=True, autojson=True):
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
584
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
585 #: A :class:`ConfigDict` for app specific configuration.
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
586 self.config = ConfigDict()
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
587 self.config._on_change = functools.partial(self.trigger_hook, 'config')
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
588 self.config.meta_set('autojson', 'validate', bool)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
589 self.config.meta_set('catchall', 'validate', bool)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
590 self.config['catchall'] = catchall
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
591 self.config['autojson'] = autojson
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
592
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
593 #: A :class:`ResourceManager` for application files
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
594 self.resources = ResourceManager()
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
595
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
596 self.routes = [] # List of installed :class:`Route` instances.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
597 self.router = Router() # Maps requests to :class:`Route` instances.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
598 self.error_handler = {}
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
599
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
600 # Core plugins
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
601 self.plugins = [] # List of installed plugins.
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
602 if self.config['autojson']:
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
603 self.install(JSONPlugin())
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
604 self.install(TemplatePlugin())
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
605
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
606 #: If true, most exceptions are caught and returned as :exc:`HTTPError`
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
607 catchall = DictProperty('config', 'catchall')
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
608
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
609 __hook_names = 'before_request', 'after_request', 'app_reset', 'config'
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
610 __hook_reversed = 'after_request'
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
611
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
612 @cached_property
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
613 def _hooks(self):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
614 return dict((name, []) for name in self.__hook_names)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
615
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
616 def add_hook(self, name, func):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
617 ''' Attach a callback to a hook. Three hooks are currently implemented:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
618
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
619 before_request
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
620 Executed once before each request. The request context is
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
621 available, but no routing has happened yet.
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
622 after_request
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
623 Executed once after each request regardless of its outcome.
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
624 app_reset
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
625 Called whenever :meth:`Bottle.reset` is called.
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
626 '''
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
627 if name in self.__hook_reversed:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
628 self._hooks[name].insert(0, func)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
629 else:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
630 self._hooks[name].append(func)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
631
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
632 def remove_hook(self, name, func):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
633 ''' Remove a callback from a hook. '''
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
634 if name in self._hooks and func in self._hooks[name]:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
635 self._hooks[name].remove(func)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
636 return True
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
637
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
638 def trigger_hook(self, __name, *args, **kwargs):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
639 ''' Trigger a hook and return a list of results. '''
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
640 return [hook(*args, **kwargs) for hook in self._hooks[__name][:]]
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
641
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
642 def hook(self, name):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
643 """ Return a decorator that attaches a callback to a hook. See
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
644 :meth:`add_hook` for details."""
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
645 def decorator(func):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
646 self.add_hook(name, func)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
647 return func
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
648 return decorator
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
649
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
650 def mount(self, prefix, app, **options):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
651 ''' Mount an application (:class:`Bottle` or plain WSGI) to a specific
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
652 URL prefix. Example::
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
653
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
654 root_app.mount('/admin/', admin_app)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
655
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
656 :param prefix: path prefix or `mount-point`. If it ends in a slash,
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
657 that slash is mandatory.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
658 :param app: an instance of :class:`Bottle` or a WSGI application.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
659
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
660 All other parameters are passed to the underlying :meth:`route` call.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
661 '''
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
662 if isinstance(app, basestring):
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
663 depr('Parameter order of Bottle.mount() changed.', True) # 0.10
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
664
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
665 segments = [p for p in prefix.split('/') if p]
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
666 if not segments: raise ValueError('Empty path prefix.')
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
667 path_depth = len(segments)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
668
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
669 def mountpoint_wrapper():
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
670 try:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
671 request.path_shift(path_depth)
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
672 rs = HTTPResponse([])
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
673 def start_response(status, headerlist, exc_info=None):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
674 if exc_info:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
675 try:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
676 _raise(*exc_info)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
677 finally:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
678 exc_info = None
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
679 rs.status = status
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
680 for name, value in headerlist: rs.add_header(name, value)
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
681 return rs.body.append
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
682 body = app(request.environ, start_response)
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
683 if body and rs.body: body = itertools.chain(rs.body, body)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
684 rs.body = body or rs.body
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
685 return rs
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
686 finally:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
687 request.path_shift(-path_depth)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
688
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
689 options.setdefault('skip', True)
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
690 options.setdefault('method', 'PROXY')
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
691 options.setdefault('mountpoint', {'prefix': prefix, 'target': app})
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
692 options['callback'] = mountpoint_wrapper
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
693
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
694 self.route('/%s/<:re:.*>' % '/'.join(segments), **options)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
695 if not prefix.endswith('/'):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
696 self.route('/' + '/'.join(segments), **options)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
697
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
698 def merge(self, routes):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
699 ''' Merge the routes of another :class:`Bottle` application or a list of
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
700 :class:`Route` objects into this application. The routes keep their
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
701 'owner', meaning that the :data:`Route.app` attribute is not
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
702 changed. '''
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
703 if isinstance(routes, Bottle):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
704 routes = routes.routes
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
705 for route in routes:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
706 self.add_route(route)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
707
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
708 def install(self, plugin):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
709 ''' Add a plugin to the list of plugins and prepare it for being
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
710 applied to all routes of this application. A plugin may be a simple
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
711 decorator or an object that implements the :class:`Plugin` API.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
712 '''
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
713 if hasattr(plugin, 'setup'): plugin.setup(self)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
714 if not callable(plugin) and not hasattr(plugin, 'apply'):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
715 raise TypeError("Plugins must be callable or implement .apply()")
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
716 self.plugins.append(plugin)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
717 self.reset()
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
718 return plugin
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
719
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
720 def uninstall(self, plugin):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
721 ''' Uninstall plugins. Pass an instance to remove a specific plugin, a type
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
722 object to remove all plugins that match that type, a string to remove
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
723 all plugins with a matching ``name`` attribute or ``True`` to remove all
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
724 plugins. Return the list of removed plugins. '''
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
725 removed, remove = [], plugin
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
726 for i, plugin in list(enumerate(self.plugins))[::-1]:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
727 if remove is True or remove is plugin or remove is type(plugin) \
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
728 or getattr(plugin, 'name', True) == remove:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
729 removed.append(plugin)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
730 del self.plugins[i]
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
731 if hasattr(plugin, 'close'): plugin.close()
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
732 if removed: self.reset()
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
733 return removed
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
734
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
735 def reset(self, route=None):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
736 ''' Reset all routes (force plugins to be re-applied) and clear all
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
737 caches. If an ID or route object is given, only that specific route
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
738 is affected. '''
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
739 if route is None: routes = self.routes
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
740 elif isinstance(route, Route): routes = [route]
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
741 else: routes = [self.routes[route]]
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
742 for route in routes: route.reset()
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
743 if DEBUG:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
744 for route in routes: route.prepare()
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
745 self.trigger_hook('app_reset')
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
746
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
747 def close(self):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
748 ''' Close the application and all installed plugins. '''
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
749 for plugin in self.plugins:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
750 if hasattr(plugin, 'close'): plugin.close()
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
751 self.stopped = True
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
752
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
753 def run(self, **kwargs):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
754 ''' Calls :func:`run` with the same parameters. '''
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
755 run(self, **kwargs)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
756
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
757 def match(self, environ):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
758 """ Search for a matching route and return a (:class:`Route` , urlargs)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
759 tuple. The second value is a dictionary with parameters extracted
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
760 from the URL. Raise :exc:`HTTPError` (404/405) on a non-match."""
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
761 return self.router.match(environ)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
762
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
763 def get_url(self, routename, **kargs):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
764 """ Return a string that matches a named route """
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
765 scriptname = request.environ.get('SCRIPT_NAME', '').strip('/') + '/'
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
766 location = self.router.build(routename, **kargs).lstrip('/')
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
767 return urljoin(urljoin('/', scriptname), location)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
768
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
769 def add_route(self, route):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
770 ''' Add a route object, but do not change the :data:`Route.app`
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
771 attribute.'''
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
772 self.routes.append(route)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
773 self.router.add(route.rule, route.method, route, name=route.name)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
774 if DEBUG: route.prepare()
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
775
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
776 def route(self, path=None, method='GET', callback=None, name=None,
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
777 apply=None, skip=None, **config):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
778 """ A decorator to bind a function to a request URL. Example::
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
779
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
780 @app.route('/hello/:name')
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
781 def hello(name):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
782 return 'Hello %s' % name
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
783
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
784 The ``:name`` part is a wildcard. See :class:`Router` for syntax
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
785 details.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
786
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
787 :param path: Request path or a list of paths to listen to. If no
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
788 path is specified, it is automatically generated from the
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
789 signature of the function.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
790 :param method: HTTP method (`GET`, `POST`, `PUT`, ...) or a list of
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
791 methods to listen to. (default: `GET`)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
792 :param callback: An optional shortcut to avoid the decorator
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
793 syntax. ``route(..., callback=func)`` equals ``route(...)(func)``
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
794 :param name: The name for this route. (default: None)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
795 :param apply: A decorator or plugin or a list of plugins. These are
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
796 applied to the route callback in addition to installed plugins.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
797 :param skip: A list of plugins, plugin classes or names. Matching
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
798 plugins are not installed to this route. ``True`` skips all.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
799
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
800 Any additional keyword arguments are stored as route-specific
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
801 configuration and passed to plugins (see :meth:`Plugin.apply`).
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
802 """
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
803 if callable(path): path, callback = None, path
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
804 plugins = makelist(apply)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
805 skiplist = makelist(skip)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
806 def decorator(callback):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
807 # TODO: Documentation and tests
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
808 if isinstance(callback, basestring): callback = load(callback)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
809 for rule in makelist(path) or yieldroutes(callback):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
810 for verb in makelist(method):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
811 verb = verb.upper()
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
812 route = Route(self, rule, verb, callback, name=name,
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
813 plugins=plugins, skiplist=skiplist, **config)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
814 self.add_route(route)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
815 return callback
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
816 return decorator(callback) if callback else decorator
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
817
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
818 def get(self, path=None, method='GET', **options):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
819 """ Equals :meth:`route`. """
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
820 return self.route(path, method, **options)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
821
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
822 def post(self, path=None, method='POST', **options):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
823 """ Equals :meth:`route` with a ``POST`` method parameter. """
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
824 return self.route(path, method, **options)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
825
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
826 def put(self, path=None, method='PUT', **options):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
827 """ Equals :meth:`route` with a ``PUT`` method parameter. """
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
828 return self.route(path, method, **options)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
829
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
830 def delete(self, path=None, method='DELETE', **options):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
831 """ Equals :meth:`route` with a ``DELETE`` method parameter. """
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
832 return self.route(path, method, **options)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
833
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
834 def error(self, code=500):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
835 """ Decorator: Register an output handler for a HTTP error code"""
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
836 def wrapper(handler):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
837 self.error_handler[int(code)] = handler
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
838 return handler
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
839 return wrapper
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
840
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
841 def default_error_handler(self, res):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
842 return tob(template(ERROR_PAGE_TEMPLATE, e=res))
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
843
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
844 def _handle(self, environ):
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
845 path = environ['bottle.raw_path'] = environ['PATH_INFO']
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
846 if py3k:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
847 try:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
848 environ['PATH_INFO'] = path.encode('latin1').decode('utf8')
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
849 except UnicodeError:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
850 return HTTPError(400, 'Invalid path string. Expected UTF-8')
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
851
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
852 try:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
853 environ['bottle.app'] = self
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
854 request.bind(environ)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
855 response.bind()
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
856 try:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
857 self.trigger_hook('before_request')
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
858 route, args = self.router.match(environ)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
859 environ['route.handle'] = route
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
860 environ['bottle.route'] = route
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
861 environ['route.url_args'] = args
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
862 return route.call(**args)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
863 finally:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
864 self.trigger_hook('after_request')
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
865
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
866 except HTTPResponse:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
867 return _e()
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
868 except RouteReset:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
869 route.reset()
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
870 return self._handle(environ)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
871 except (KeyboardInterrupt, SystemExit, MemoryError):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
872 raise
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
873 except Exception:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
874 if not self.catchall: raise
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
875 stacktrace = format_exc()
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
876 environ['wsgi.errors'].write(stacktrace)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
877 return HTTPError(500, "Internal Server Error", _e(), stacktrace)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
878
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
879 def _cast(self, out, peek=None):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
880 """ Try to convert the parameter into something WSGI compatible and set
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
881 correct HTTP headers when possible.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
882 Support: False, str, unicode, dict, HTTPResponse, HTTPError, file-like,
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
883 iterable of strings and iterable of unicodes
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
884 """
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
885
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
886 # Empty output is done here
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
887 if not out:
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
888 if 'Content-Length' not in response:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
889 response['Content-Length'] = 0
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
890 return []
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
891 # Join lists of byte or unicode strings. Mixed lists are NOT supported
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
892 if isinstance(out, (tuple, list))\
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
893 and isinstance(out[0], (bytes, unicode)):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
894 out = out[0][0:0].join(out) # b'abc'[0:0] -> b''
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
895 # Encode unicode strings
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
896 if isinstance(out, unicode):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
897 out = out.encode(response.charset)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
898 # Byte Strings are just returned
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
899 if isinstance(out, bytes):
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
900 if 'Content-Length' not in response:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
901 response['Content-Length'] = len(out)
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
902 return [out]
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
903 # HTTPError or HTTPException (recursive, because they may wrap anything)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
904 # TODO: Handle these explicitly in handle() or make them iterable.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
905 if isinstance(out, HTTPError):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
906 out.apply(response)
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
907 out = self.error_handler.get(out.status_code, self.default_error_handler)(out)
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
908 return self._cast(out)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
909 if isinstance(out, HTTPResponse):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
910 out.apply(response)
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
911 return self._cast(out.body)
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
912
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
913 # File-like objects.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
914 if hasattr(out, 'read'):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
915 if 'wsgi.file_wrapper' in request.environ:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
916 return request.environ['wsgi.file_wrapper'](out)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
917 elif hasattr(out, 'close') or not hasattr(out, '__iter__'):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
918 return WSGIFileWrapper(out)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
919
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
920 # Handle Iterables. We peek into them to detect their inner type.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
921 try:
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
922 iout = iter(out)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
923 first = next(iout)
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
924 while not first:
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
925 first = next(iout)
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
926 except StopIteration:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
927 return self._cast('')
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
928 except HTTPResponse:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
929 first = _e()
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
930 except (KeyboardInterrupt, SystemExit, MemoryError):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
931 raise
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
932 except Exception:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
933 if not self.catchall: raise
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
934 first = HTTPError(500, 'Unhandled exception', _e(), format_exc())
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
935
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
936 # These are the inner types allowed in iterator or generator objects.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
937 if isinstance(first, HTTPResponse):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
938 return self._cast(first)
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
939 elif isinstance(first, bytes):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
940 new_iter = itertools.chain([first], iout)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
941 elif isinstance(first, unicode):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
942 encoder = lambda x: x.encode(response.charset)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
943 new_iter = imap(encoder, itertools.chain([first], iout))
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
944 else:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
945 msg = 'Unsupported response type: %s' % type(first)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
946 return self._cast(HTTPError(500, msg))
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
947 if hasattr(out, 'close'):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
948 new_iter = _closeiter(new_iter, out.close)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
949 return new_iter
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
950
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
951 def wsgi(self, environ, start_response):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
952 """ The bottle WSGI-interface. """
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
953 try:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
954 out = self._cast(self._handle(environ))
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
955 # rfc2616 section 4.3
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
956 if response._status_code in (100, 101, 204, 304)\
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
957 or environ['REQUEST_METHOD'] == 'HEAD':
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
958 if hasattr(out, 'close'): out.close()
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
959 out = []
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
960 start_response(response._status_line, response.headerlist)
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
961 return out
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
962 except (KeyboardInterrupt, SystemExit, MemoryError):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
963 raise
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
964 except Exception:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
965 if not self.catchall: raise
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
966 err = '<h1>Critical error while processing request: %s</h1>' \
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
967 % html_escape(environ.get('PATH_INFO', '/'))
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
968 if DEBUG:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
969 err += '<h2>Error:</h2>\n<pre>\n%s\n</pre>\n' \
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
970 '<h2>Traceback:</h2>\n<pre>\n%s\n</pre>\n' \
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
971 % (html_escape(repr(_e())), html_escape(format_exc()))
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
972 environ['wsgi.errors'].write(err)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
973 headers = [('Content-Type', 'text/html; charset=UTF-8')]
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
974 start_response('500 INTERNAL SERVER ERROR', headers, sys.exc_info())
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
975 return [tob(err)]
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
976
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
977 def __call__(self, environ, start_response):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
978 ''' Each instance of :class:'Bottle' is a WSGI application. '''
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
979 return self.wsgi(environ, start_response)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
980
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
981
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
982
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
983
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
984
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
985
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
986 ###############################################################################
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
987 # HTTP and WSGI Tools ##########################################################
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
988 ###############################################################################
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
989
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
990 class BaseRequest(object):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
991 """ A wrapper for WSGI environment dictionaries that adds a lot of
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
992 convenient access methods and properties. Most of them are read-only.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
993
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
994 Adding new attributes to a request actually adds them to the environ
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
995 dictionary (as 'bottle.request.ext.<name>'). This is the recommended
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
996 way to store and access request-specific data.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
997 """
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
998
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
999 __slots__ = ('environ')
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1000
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1001 #: Maximum size of memory buffer for :attr:`body` in bytes.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1002 MEMFILE_MAX = 102400
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1003
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1004 def __init__(self, environ=None):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1005 """ Wrap a WSGI environ dictionary. """
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1006 #: The wrapped WSGI environ dictionary. This is the only real attribute.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1007 #: All other attributes actually are read-only properties.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1008 self.environ = {} if environ is None else environ
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1009 self.environ['bottle.request'] = self
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1010
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1011 @DictProperty('environ', 'bottle.app', read_only=True)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1012 def app(self):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1013 ''' Bottle application handling this request. '''
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1014 raise RuntimeError('This request is not connected to an application.')
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1015
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1016 @DictProperty('environ', 'bottle.route', read_only=True)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1017 def route(self):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1018 """ The bottle :class:`Route` object that matches this request. """
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1019 raise RuntimeError('This request is not connected to a route.')
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1020
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1021 @DictProperty('environ', 'route.url_args', read_only=True)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1022 def url_args(self):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1023 """ The arguments extracted from the URL. """
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1024 raise RuntimeError('This request is not connected to a route.')
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1025
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1026 @property
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1027 def path(self):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1028 ''' The value of ``PATH_INFO`` with exactly one prefixed slash (to fix
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1029 broken clients and avoid the "empty path" edge case). '''
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1030 return '/' + self.environ.get('PATH_INFO','').lstrip('/')
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1031
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1032 @property
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1033 def method(self):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1034 ''' The ``REQUEST_METHOD`` value as an uppercase string. '''
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1035 return self.environ.get('REQUEST_METHOD', 'GET').upper()
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1036
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1037 @DictProperty('environ', 'bottle.request.headers', read_only=True)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1038 def headers(self):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1039 ''' A :class:`WSGIHeaderDict` that provides case-insensitive access to
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1040 HTTP request headers. '''
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1041 return WSGIHeaderDict(self.environ)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1042
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1043 def get_header(self, name, default=None):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1044 ''' Return the value of a request header, or a given default value. '''
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1045 return self.headers.get(name, default)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1046
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1047 @DictProperty('environ', 'bottle.request.cookies', read_only=True)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1048 def cookies(self):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1049 """ Cookies parsed into a :class:`FormsDict`. Signed cookies are NOT
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1050 decoded. Use :meth:`get_cookie` if you expect signed cookies. """
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1051 cookies = SimpleCookie(self.environ.get('HTTP_COOKIE','')).values()
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1052 return FormsDict((c.key, c.value) for c in cookies)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1053
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1054 def get_cookie(self, key, default=None, secret=None):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1055 """ Return the content of a cookie. To read a `Signed Cookie`, the
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1056 `secret` must match the one used to create the cookie (see
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1057 :meth:`BaseResponse.set_cookie`). If anything goes wrong (missing
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1058 cookie or wrong signature), return a default value. """
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1059 value = self.cookies.get(key)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1060 if secret and value:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1061 dec = cookie_decode(value, secret) # (key, value) tuple or None
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1062 return dec[1] if dec and dec[0] == key else default
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1063 return value or default
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1064
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1065 @DictProperty('environ', 'bottle.request.query', read_only=True)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1066 def query(self):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1067 ''' The :attr:`query_string` parsed into a :class:`FormsDict`. These
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1068 values are sometimes called "URL arguments" or "GET parameters", but
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1069 not to be confused with "URL wildcards" as they are provided by the
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1070 :class:`Router`. '''
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1071 get = self.environ['bottle.get'] = FormsDict()
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1072 pairs = _parse_qsl(self.environ.get('QUERY_STRING', ''))
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1073 for key, value in pairs:
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1074 get[key] = value
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1075 return get
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1076
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1077 @DictProperty('environ', 'bottle.request.forms', read_only=True)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1078 def forms(self):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1079 """ Form values parsed from an `url-encoded` or `multipart/form-data`
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1080 encoded POST or PUT request body. The result is returned as a
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1081 :class:`FormsDict`. All keys and values are strings. File uploads
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1082 are stored separately in :attr:`files`. """
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1083 forms = FormsDict()
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1084 for name, item in self.POST.allitems():
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1085 if not isinstance(item, FileUpload):
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1086 forms[name] = item
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1087 return forms
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1088
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1089 @DictProperty('environ', 'bottle.request.params', read_only=True)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1090 def params(self):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1091 """ A :class:`FormsDict` with the combined values of :attr:`query` and
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1092 :attr:`forms`. File uploads are stored in :attr:`files`. """
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1093 params = FormsDict()
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1094 for key, value in self.query.allitems():
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1095 params[key] = value
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1096 for key, value in self.forms.allitems():
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1097 params[key] = value
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1098 return params
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1099
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1100 @DictProperty('environ', 'bottle.request.files', read_only=True)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1101 def files(self):
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1102 """ File uploads parsed from `multipart/form-data` encoded POST or PUT
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1103 request body. The values are instances of :class:`FileUpload`.
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1104
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1105 """
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1106 files = FormsDict()
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1107 for name, item in self.POST.allitems():
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1108 if isinstance(item, FileUpload):
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1109 files[name] = item
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1110 return files
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1111
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1112 @DictProperty('environ', 'bottle.request.json', read_only=True)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1113 def json(self):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1114 ''' If the ``Content-Type`` header is ``application/json``, this
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1115 property holds the parsed content of the request body. Only requests
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1116 smaller than :attr:`MEMFILE_MAX` are processed to avoid memory
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1117 exhaustion. '''
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1118 ctype = self.environ.get('CONTENT_TYPE', '').lower().split(';')[0]
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1119 if ctype == 'application/json':
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1120 b = self._get_body_string()
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1121 if not b:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1122 return None
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1123 return json_loads(b)
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1124 return None
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1125
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1126 def _iter_body(self, read, bufsize):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1127 maxread = max(0, self.content_length)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1128 while maxread:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1129 part = read(min(maxread, bufsize))
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1130 if not part: break
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1131 yield part
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1132 maxread -= len(part)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1133
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1134 def _iter_chunked(self, read, bufsize):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1135 err = HTTPError(400, 'Error while parsing chunked transfer body.')
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1136 rn, sem, bs = tob('\r\n'), tob(';'), tob('')
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1137 while True:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1138 header = read(1)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1139 while header[-2:] != rn:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1140 c = read(1)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1141 header += c
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1142 if not c: raise err
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1143 if len(header) > bufsize: raise err
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1144 size, _, _ = header.partition(sem)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1145 try:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1146 maxread = int(tonat(size.strip()), 16)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1147 except ValueError:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1148 raise err
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1149 if maxread == 0: break
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1150 buff = bs
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1151 while maxread > 0:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1152 if not buff:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1153 buff = read(min(maxread, bufsize))
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1154 part, buff = buff[:maxread], buff[maxread:]
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1155 if not part: raise err
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1156 yield part
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1157 maxread -= len(part)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1158 if read(2) != rn:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1159 raise err
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1160
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1161 @DictProperty('environ', 'bottle.request.body', read_only=True)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1162 def _body(self):
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1163 body_iter = self._iter_chunked if self.chunked else self._iter_body
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1164 read_func = self.environ['wsgi.input'].read
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1165 body, body_size, is_temp_file = BytesIO(), 0, False
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1166 for part in body_iter(read_func, self.MEMFILE_MAX):
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1167 body.write(part)
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1168 body_size += len(part)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1169 if not is_temp_file and body_size > self.MEMFILE_MAX:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1170 body, tmp = TemporaryFile(mode='w+b'), body
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1171 body.write(tmp.getvalue())
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1172 del tmp
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1173 is_temp_file = True
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1174 self.environ['wsgi.input'] = body
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1175 body.seek(0)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1176 return body
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1177
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1178 def _get_body_string(self):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1179 ''' read body until content-length or MEMFILE_MAX into a string. Raise
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1180 HTTPError(413) on requests that are to large. '''
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1181 clen = self.content_length
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1182 if clen > self.MEMFILE_MAX:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1183 raise HTTPError(413, 'Request to large')
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1184 if clen < 0: clen = self.MEMFILE_MAX + 1
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1185 data = self.body.read(clen)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1186 if len(data) > self.MEMFILE_MAX: # Fail fast
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1187 raise HTTPError(413, 'Request to large')
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1188 return data
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1189
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1190 @property
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1191 def body(self):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1192 """ The HTTP request body as a seek-able file-like object. Depending on
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1193 :attr:`MEMFILE_MAX`, this is either a temporary file or a
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1194 :class:`io.BytesIO` instance. Accessing this property for the first
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1195 time reads and replaces the ``wsgi.input`` environ variable.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1196 Subsequent accesses just do a `seek(0)` on the file object. """
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1197 self._body.seek(0)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1198 return self._body
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1199
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1200 @property
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1201 def chunked(self):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1202 ''' True if Chunked transfer encoding was. '''
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1203 return 'chunked' in self.environ.get('HTTP_TRANSFER_ENCODING', '').lower()
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1204
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1205 #: An alias for :attr:`query`.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1206 GET = query
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1207
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1208 @DictProperty('environ', 'bottle.request.post', read_only=True)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1209 def POST(self):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1210 """ The values of :attr:`forms` and :attr:`files` combined into a single
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1211 :class:`FormsDict`. Values are either strings (form values) or
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1212 instances of :class:`cgi.FieldStorage` (file uploads).
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1213 """
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1214 post = FormsDict()
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1215 # We default to application/x-www-form-urlencoded for everything that
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1216 # is not multipart and take the fast path (also: 3.1 workaround)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1217 if not self.content_type.startswith('multipart/'):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1218 pairs = _parse_qsl(tonat(self._get_body_string(), 'latin1'))
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1219 for key, value in pairs:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1220 post[key] = value
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1221 return post
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1222
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1223 safe_env = {'QUERY_STRING':''} # Build a safe environment for cgi
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1224 for key in ('REQUEST_METHOD', 'CONTENT_TYPE', 'CONTENT_LENGTH'):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1225 if key in self.environ: safe_env[key] = self.environ[key]
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1226 args = dict(fp=self.body, environ=safe_env, keep_blank_values=True)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1227 if py31:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1228 args['fp'] = NCTextIOWrapper(args['fp'], encoding='utf8',
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1229 newline='\n')
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1230 elif py3k:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1231 args['encoding'] = 'utf8'
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1232 data = cgi.FieldStorage(**args)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1233 self['_cgi.FieldStorage'] = data #http://bugs.python.org/issue18394#msg207958
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1234 data = data.list or []
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1235 for item in data:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1236 if item.filename:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1237 post[item.name] = FileUpload(item.file, item.name,
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1238 item.filename, item.headers)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1239 else:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1240 post[item.name] = item.value
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1241 return post
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1242
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1243 @property
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1244 def url(self):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1245 """ The full request URI including hostname and scheme. If your app
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1246 lives behind a reverse proxy or load balancer and you get confusing
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1247 results, make sure that the ``X-Forwarded-Host`` header is set
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1248 correctly. """
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1249 return self.urlparts.geturl()
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1250
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1251 @DictProperty('environ', 'bottle.request.urlparts', read_only=True)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1252 def urlparts(self):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1253 ''' The :attr:`url` string as an :class:`urlparse.SplitResult` tuple.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1254 The tuple contains (scheme, host, path, query_string and fragment),
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1255 but the fragment is always empty because it is not visible to the
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1256 server. '''
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1257 env = self.environ
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1258 http = env.get('HTTP_X_FORWARDED_PROTO') or env.get('wsgi.url_scheme', 'http')
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1259 host = env.get('HTTP_X_FORWARDED_HOST') or env.get('HTTP_HOST')
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1260 if not host:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1261 # HTTP 1.1 requires a Host-header. This is for HTTP/1.0 clients.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1262 host = env.get('SERVER_NAME', '127.0.0.1')
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1263 port = env.get('SERVER_PORT')
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1264 if port and port != ('80' if http == 'http' else '443'):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1265 host += ':' + port
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1266 path = urlquote(self.fullpath)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1267 return UrlSplitResult(http, host, path, env.get('QUERY_STRING'), '')
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1268
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1269 @property
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1270 def fullpath(self):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1271 """ Request path including :attr:`script_name` (if present). """
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1272 return urljoin(self.script_name, self.path.lstrip('/'))
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1273
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1274 @property
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1275 def query_string(self):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1276 """ The raw :attr:`query` part of the URL (everything in between ``?``
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1277 and ``#``) as a string. """
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1278 return self.environ.get('QUERY_STRING', '')
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1279
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1280 @property
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1281 def script_name(self):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1282 ''' The initial portion of the URL's `path` that was removed by a higher
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1283 level (server or routing middleware) before the application was
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1284 called. This script path is returned with leading and tailing
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1285 slashes. '''
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1286 script_name = self.environ.get('SCRIPT_NAME', '').strip('/')
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1287 return '/' + script_name + '/' if script_name else '/'
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1288
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1289 def path_shift(self, shift=1):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1290 ''' Shift path segments from :attr:`path` to :attr:`script_name` and
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1291 vice versa.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1292
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1293 :param shift: The number of path segments to shift. May be negative
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1294 to change the shift direction. (default: 1)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1295 '''
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1296 script = self.environ.get('SCRIPT_NAME','/')
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1297 self['SCRIPT_NAME'], self['PATH_INFO'] = path_shift(script, self.path, shift)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1298
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1299 @property
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1300 def content_length(self):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1301 ''' The request body length as an integer. The client is responsible to
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1302 set this header. Otherwise, the real length of the body is unknown
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1303 and -1 is returned. In this case, :attr:`body` will be empty. '''
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1304 return int(self.environ.get('CONTENT_LENGTH') or -1)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1305
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1306 @property
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1307 def content_type(self):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1308 ''' The Content-Type header as a lowercase-string (default: empty). '''
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1309 return self.environ.get('CONTENT_TYPE', '').lower()
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1310
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1311 @property
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1312 def is_xhr(self):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1313 ''' True if the request was triggered by a XMLHttpRequest. This only
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1314 works with JavaScript libraries that support the `X-Requested-With`
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1315 header (most of the popular libraries do). '''
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1316 requested_with = self.environ.get('HTTP_X_REQUESTED_WITH','')
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1317 return requested_with.lower() == 'xmlhttprequest'
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1318
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1319 @property
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1320 def is_ajax(self):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1321 ''' Alias for :attr:`is_xhr`. "Ajax" is not the right term. '''
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1322 return self.is_xhr
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1323
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1324 @property
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1325 def auth(self):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1326 """ HTTP authentication data as a (user, password) tuple. This
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1327 implementation currently supports basic (not digest) authentication
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1328 only. If the authentication happened at a higher level (e.g. in the
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1329 front web-server or a middleware), the password field is None, but
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1330 the user field is looked up from the ``REMOTE_USER`` environ
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1331 variable. On any errors, None is returned. """
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1332 basic = parse_auth(self.environ.get('HTTP_AUTHORIZATION',''))
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1333 if basic: return basic
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1334 ruser = self.environ.get('REMOTE_USER')
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1335 if ruser: return (ruser, None)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1336 return None
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1337
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1338 @property
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1339 def remote_route(self):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1340 """ A list of all IPs that were involved in this request, starting with
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1341 the client IP and followed by zero or more proxies. This does only
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1342 work if all proxies support the ```X-Forwarded-For`` header. Note
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1343 that this information can be forged by malicious clients. """
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1344 proxy = self.environ.get('HTTP_X_FORWARDED_FOR')
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1345 if proxy: return [ip.strip() for ip in proxy.split(',')]
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1346 remote = self.environ.get('REMOTE_ADDR')
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1347 return [remote] if remote else []
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1348
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1349 @property
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1350 def remote_addr(self):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1351 """ The client IP as a string. Note that this information can be forged
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1352 by malicious clients. """
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1353 route = self.remote_route
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1354 return route[0] if route else None
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1355
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1356 def copy(self):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1357 """ Return a new :class:`Request` with a shallow :attr:`environ` copy. """
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1358 return Request(self.environ.copy())
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1359
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1360 def get(self, value, default=None): return self.environ.get(value, default)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1361 def __getitem__(self, key): return self.environ[key]
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1362 def __delitem__(self, key): self[key] = ""; del(self.environ[key])
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1363 def __iter__(self): return iter(self.environ)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1364 def __len__(self): return len(self.environ)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1365 def keys(self): return self.environ.keys()
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1366 def __setitem__(self, key, value):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1367 """ Change an environ value and clear all caches that depend on it. """
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1368
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1369 if self.environ.get('bottle.request.readonly'):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1370 raise KeyError('The environ dictionary is read-only.')
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1371
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1372 self.environ[key] = value
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1373 todelete = ()
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1374
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1375 if key == 'wsgi.input':
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1376 todelete = ('body', 'forms', 'files', 'params', 'post', 'json')
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1377 elif key == 'QUERY_STRING':
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1378 todelete = ('query', 'params')
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1379 elif key.startswith('HTTP_'):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1380 todelete = ('headers', 'cookies')
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1381
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1382 for key in todelete:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1383 self.environ.pop('bottle.request.'+key, None)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1384
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1385 def __repr__(self):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1386 return '<%s: %s %s>' % (self.__class__.__name__, self.method, self.url)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1387
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1388 def __getattr__(self, name):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1389 ''' Search in self.environ for additional user defined attributes. '''
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1390 try:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1391 var = self.environ['bottle.request.ext.%s'%name]
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1392 return var.__get__(self) if hasattr(var, '__get__') else var
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1393 except KeyError:
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1394 raise AttributeError('Attribute %r not defined.' % name)
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1395
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1396 def __setattr__(self, name, value):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1397 if name == 'environ': return object.__setattr__(self, name, value)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1398 self.environ['bottle.request.ext.%s'%name] = value
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1399
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1400
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1401
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1402
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1403 def _hkey(s):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1404 return s.title().replace('_','-')
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1405
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1406
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1407 class HeaderProperty(object):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1408 def __init__(self, name, reader=None, writer=str, default=''):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1409 self.name, self.default = name, default
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1410 self.reader, self.writer = reader, writer
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1411 self.__doc__ = 'Current value of the %r header.' % name.title()
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1412
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1413 def __get__(self, obj, cls):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1414 if obj is None: return self
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1415 value = obj.headers.get(self.name, self.default)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1416 return self.reader(value) if self.reader else value
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1417
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1418 def __set__(self, obj, value):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1419 obj.headers[self.name] = self.writer(value)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1420
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1421 def __delete__(self, obj):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1422 del obj.headers[self.name]
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1423
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1424
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1425 class BaseResponse(object):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1426 """ Storage class for a response body as well as headers and cookies.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1427
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1428 This class does support dict-like case-insensitive item-access to
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1429 headers, but is NOT a dict. Most notably, iterating over a response
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1430 yields parts of the body and not the headers.
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1431
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1432 :param body: The response body as one of the supported types.
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1433 :param status: Either an HTTP status code (e.g. 200) or a status line
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1434 including the reason phrase (e.g. '200 OK').
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1435 :param headers: A dictionary or a list of name-value pairs.
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1436
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1437 Additional keyword arguments are added to the list of headers.
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1438 Underscores in the header name are replaced with dashes.
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1439 """
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1440
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1441 default_status = 200
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1442 default_content_type = 'text/html; charset=UTF-8'
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1443
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1444 # Header blacklist for specific response codes
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1445 # (rfc2616 section 10.2.3 and 10.3.5)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1446 bad_headers = {
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1447 204: set(('Content-Type',)),
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1448 304: set(('Allow', 'Content-Encoding', 'Content-Language',
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1449 'Content-Length', 'Content-Range', 'Content-Type',
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1450 'Content-Md5', 'Last-Modified'))}
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1451
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1452 def __init__(self, body='', status=None, headers=None, **more_headers):
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1453 self._cookies = None
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1454 self._headers = {}
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1455 self.body = body
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1456 self.status = status or self.default_status
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1457 if headers:
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1458 if isinstance(headers, dict):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1459 headers = headers.items()
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1460 for name, value in headers:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1461 self.add_header(name, value)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1462 if more_headers:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1463 for name, value in more_headers.items():
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1464 self.add_header(name, value)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1465
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1466 def copy(self, cls=None):
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1467 ''' Returns a copy of self. '''
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1468 cls = cls or BaseResponse
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1469 assert issubclass(cls, BaseResponse)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1470 copy = cls()
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1471 copy.status = self.status
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1472 copy._headers = dict((k, v[:]) for (k, v) in self._headers.items())
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1473 if self._cookies:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1474 copy._cookies = SimpleCookie()
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1475 copy._cookies.load(self._cookies.output(header=''))
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1476 return copy
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1477
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1478 def __iter__(self):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1479 return iter(self.body)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1480
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1481 def close(self):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1482 if hasattr(self.body, 'close'):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1483 self.body.close()
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1484
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1485 @property
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1486 def status_line(self):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1487 ''' The HTTP status line as a string (e.g. ``404 Not Found``).'''
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1488 return self._status_line
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1489
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1490 @property
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1491 def status_code(self):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1492 ''' The HTTP status code as an integer (e.g. 404).'''
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1493 return self._status_code
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1494
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1495 def _set_status(self, status):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1496 if isinstance(status, int):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1497 code, status = status, _HTTP_STATUS_LINES.get(status)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1498 elif ' ' in status:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1499 status = status.strip()
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1500 code = int(status.split()[0])
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1501 else:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1502 raise ValueError('String status line without a reason phrase.')
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1503 if not 100 <= code <= 999: raise ValueError('Status code out of range.')
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1504 self._status_code = code
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1505 self._status_line = str(status or ('%d Unknown' % code))
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1506
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1507 def _get_status(self):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1508 return self._status_line
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1509
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1510 status = property(_get_status, _set_status, None,
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1511 ''' A writeable property to change the HTTP response status. It accepts
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1512 either a numeric code (100-999) or a string with a custom reason
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1513 phrase (e.g. "404 Brain not found"). Both :data:`status_line` and
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1514 :data:`status_code` are updated accordingly. The return value is
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1515 always a status string. ''')
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1516 del _get_status, _set_status
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1517
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1518 @property
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1519 def headers(self):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1520 ''' An instance of :class:`HeaderDict`, a case-insensitive dict-like
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1521 view on the response headers. '''
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1522 hdict = HeaderDict()
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1523 hdict.dict = self._headers
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1524 return hdict
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1525
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1526 def __contains__(self, name): return _hkey(name) in self._headers
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1527 def __delitem__(self, name): del self._headers[_hkey(name)]
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1528 def __getitem__(self, name): return self._headers[_hkey(name)][-1]
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1529 def __setitem__(self, name, value): self._headers[_hkey(name)] = [str(value)]
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1530
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1531 def get_header(self, name, default=None):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1532 ''' Return the value of a previously defined header. If there is no
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1533 header with that name, return a default value. '''
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1534 return self._headers.get(_hkey(name), [default])[-1]
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1535
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1536 def set_header(self, name, value):
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1537 ''' Create a new response header, replacing any previously defined
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1538 headers with the same name. '''
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1539 self._headers[_hkey(name)] = [str(value)]
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1540
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1541 def add_header(self, name, value):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1542 ''' Add an additional response header, not removing duplicates. '''
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1543 self._headers.setdefault(_hkey(name), []).append(str(value))
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1544
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1545 def iter_headers(self):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1546 ''' Yield (header, value) tuples, skipping headers that are not
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1547 allowed with the current response status code. '''
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1548 return self.headerlist
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1549
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1550 @property
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1551 def headerlist(self):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1552 ''' WSGI conform list of (header, value) tuples. '''
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1553 out = []
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1554 headers = list(self._headers.items())
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1555 if 'Content-Type' not in self._headers:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1556 headers.append(('Content-Type', [self.default_content_type]))
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1557 if self._status_code in self.bad_headers:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1558 bad_headers = self.bad_headers[self._status_code]
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1559 headers = [h for h in headers if h[0] not in bad_headers]
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1560 out += [(name, val) for name, vals in headers for val in vals]
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1561 if self._cookies:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1562 for c in self._cookies.values():
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1563 out.append(('Set-Cookie', c.OutputString()))
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1564 return out
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1565
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1566 content_type = HeaderProperty('Content-Type')
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1567 content_length = HeaderProperty('Content-Length', reader=int)
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1568 expires = HeaderProperty('Expires',
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1569 reader=lambda x: datetime.utcfromtimestamp(parse_date(x)),
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1570 writer=lambda x: http_date(x))
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1571
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1572 @property
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1573 def charset(self, default='UTF-8'):
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1574 """ Return the charset specified in the content-type header (default: utf8). """
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1575 if 'charset=' in self.content_type:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1576 return self.content_type.split('charset=')[-1].split(';')[0].strip()
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1577 return default
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1578
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1579 def set_cookie(self, name, value, secret=None, **options):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1580 ''' Create a new cookie or replace an old one. If the `secret` parameter is
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1581 set, create a `Signed Cookie` (described below).
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1582
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1583 :param name: the name of the cookie.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1584 :param value: the value of the cookie.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1585 :param secret: a signature key required for signed cookies.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1586
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1587 Additionally, this method accepts all RFC 2109 attributes that are
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1588 supported by :class:`cookie.Morsel`, including:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1589
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1590 :param max_age: maximum age in seconds. (default: None)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1591 :param expires: a datetime object or UNIX timestamp. (default: None)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1592 :param domain: the domain that is allowed to read the cookie.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1593 (default: current domain)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1594 :param path: limits the cookie to a given path (default: current path)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1595 :param secure: limit the cookie to HTTPS connections (default: off).
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1596 :param httponly: prevents client-side javascript to read this cookie
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1597 (default: off, requires Python 2.6 or newer).
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1598
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1599 If neither `expires` nor `max_age` is set (default), the cookie will
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1600 expire at the end of the browser session (as soon as the browser
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1601 window is closed).
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1602
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1603 Signed cookies may store any pickle-able object and are
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1604 cryptographically signed to prevent manipulation. Keep in mind that
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1605 cookies are limited to 4kb in most browsers.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1606
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1607 Warning: Signed cookies are not encrypted (the client can still see
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1608 the content) and not copy-protected (the client can restore an old
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1609 cookie). The main intention is to make pickling and unpickling
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1610 save, not to store secret information at client side.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1611 '''
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1612 if not self._cookies:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1613 self._cookies = SimpleCookie()
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1614
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1615 if secret:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1616 value = touni(cookie_encode((name, value), secret))
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1617 elif not isinstance(value, basestring):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1618 raise TypeError('Secret key missing for non-string Cookie.')
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1619
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1620 if len(value) > 4096: raise ValueError('Cookie value to long.')
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1621 self._cookies[name] = value
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1622
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1623 for key, value in options.items():
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1624 if key == 'max_age':
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1625 if isinstance(value, timedelta):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1626 value = value.seconds + value.days * 24 * 3600
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1627 if key == 'expires':
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1628 if isinstance(value, (datedate, datetime)):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1629 value = value.timetuple()
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1630 elif isinstance(value, (int, float)):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1631 value = time.gmtime(value)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1632 value = time.strftime("%a, %d %b %Y %H:%M:%S GMT", value)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1633 self._cookies[name][key.replace('_', '-')] = value
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1634
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1635 def delete_cookie(self, key, **kwargs):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1636 ''' Delete a cookie. Be sure to use the same `domain` and `path`
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1637 settings as used to create the cookie. '''
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1638 kwargs['max_age'] = -1
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1639 kwargs['expires'] = 0
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1640 self.set_cookie(key, '', **kwargs)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1641
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1642 def __repr__(self):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1643 out = ''
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1644 for name, value in self.headerlist:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1645 out += '%s: %s\n' % (name.title(), value.strip())
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1646 return out
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1647
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1648
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1649 def local_property(name=None):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1650 if name: depr('local_property() is deprecated and will be removed.') #0.12
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1651 ls = threading.local()
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1652 def fget(self):
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1653 try: return ls.var
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1654 except AttributeError:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1655 raise RuntimeError("Request context not initialized.")
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1656 def fset(self, value): ls.var = value
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1657 def fdel(self): del ls.var
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1658 return property(fget, fset, fdel, 'Thread-local property')
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1659
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1660
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1661 class LocalRequest(BaseRequest):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1662 ''' A thread-local subclass of :class:`BaseRequest` with a different
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1663 set of attributes for each thread. There is usually only one global
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1664 instance of this class (:data:`request`). If accessed during a
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1665 request/response cycle, this instance always refers to the *current*
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1666 request (even on a multithreaded server). '''
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1667 bind = BaseRequest.__init__
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1668 environ = local_property()
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1669
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1670
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1671 class LocalResponse(BaseResponse):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1672 ''' A thread-local subclass of :class:`BaseResponse` with a different
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1673 set of attributes for each thread. There is usually only one global
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1674 instance of this class (:data:`response`). Its attributes are used
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1675 to build the HTTP response at the end of the request/response cycle.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1676 '''
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1677 bind = BaseResponse.__init__
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1678 _status_line = local_property()
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1679 _status_code = local_property()
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1680 _cookies = local_property()
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1681 _headers = local_property()
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1682 body = local_property()
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1683
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1684
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1685 Request = BaseRequest
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1686 Response = BaseResponse
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1687
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1688
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1689 class HTTPResponse(Response, BottleException):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1690 def __init__(self, body='', status=None, headers=None, **more_headers):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1691 super(HTTPResponse, self).__init__(body, status, headers, **more_headers)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1692
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1693 def apply(self, response):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1694 response._status_code = self._status_code
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1695 response._status_line = self._status_line
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1696 response._headers = self._headers
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1697 response._cookies = self._cookies
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1698 response.body = self.body
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1699
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1700
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1701 class HTTPError(HTTPResponse):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1702 default_status = 500
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1703 def __init__(self, status=None, body=None, exception=None, traceback=None,
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1704 **options):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1705 self.exception = exception
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1706 self.traceback = traceback
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1707 super(HTTPError, self).__init__(body, status, **options)
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1708
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1709
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1710
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1711
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1712
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1713 ###############################################################################
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1714 # Plugins ######################################################################
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1715 ###############################################################################
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1716
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1717 class PluginError(BottleException): pass
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1718
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1719
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1720 class JSONPlugin(object):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1721 name = 'json'
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1722 api = 2
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1723
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1724 def __init__(self, json_dumps=json_dumps):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1725 self.json_dumps = json_dumps
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1726
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1727 def apply(self, callback, route):
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1728 dumps = self.json_dumps
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1729 if not dumps: return callback
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1730 def wrapper(*a, **ka):
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1731 try:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1732 rv = callback(*a, **ka)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1733 except HTTPError:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1734 rv = _e()
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1735
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1736 if isinstance(rv, dict):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1737 #Attempt to serialize, raises exception on failure
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1738 json_response = dumps(rv)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1739 #Set content type only if serialization succesful
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1740 response.content_type = 'application/json'
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1741 return json_response
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1742 elif isinstance(rv, HTTPResponse) and isinstance(rv.body, dict):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1743 rv.body = dumps(rv.body)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1744 rv.content_type = 'application/json'
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1745 return rv
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1746
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1747 return wrapper
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1748
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1749
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1750 class TemplatePlugin(object):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1751 ''' This plugin applies the :func:`view` decorator to all routes with a
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1752 `template` config parameter. If the parameter is a tuple, the second
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1753 element must be a dict with additional options (e.g. `template_engine`)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1754 or default variables for the template. '''
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1755 name = 'template'
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1756 api = 2
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1757
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1758 def apply(self, callback, route):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1759 conf = route.config.get('template')
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1760 if isinstance(conf, (tuple, list)) and len(conf) == 2:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1761 return view(conf[0], **conf[1])(callback)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1762 elif isinstance(conf, str):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1763 return view(conf)(callback)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1764 else:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1765 return callback
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1766
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1767
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1768 #: Not a plugin, but part of the plugin API. TODO: Find a better place.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1769 class _ImportRedirect(object):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1770 def __init__(self, name, impmask):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1771 ''' Create a virtual package that redirects imports (see PEP 302). '''
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1772 self.name = name
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1773 self.impmask = impmask
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1774 self.module = sys.modules.setdefault(name, imp.new_module(name))
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1775 self.module.__dict__.update({'__file__': __file__, '__path__': [],
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1776 '__all__': [], '__loader__': self})
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1777 sys.meta_path.append(self)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1778
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1779 def find_module(self, fullname, path=None):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1780 if '.' not in fullname: return
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1781 packname = fullname.rsplit('.', 1)[0]
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1782 if packname != self.name: return
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1783 return self
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1784
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1785 def load_module(self, fullname):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1786 if fullname in sys.modules: return sys.modules[fullname]
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1787 modname = fullname.rsplit('.', 1)[1]
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1788 realname = self.impmask % modname
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1789 __import__(realname)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1790 module = sys.modules[fullname] = sys.modules[realname]
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1791 setattr(self.module, modname, module)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1792 module.__loader__ = self
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1793 return module
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1794
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1795
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1796
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1797
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1798
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1799
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1800 ###############################################################################
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1801 # Common Utilities #############################################################
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1802 ###############################################################################
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1803
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1804
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1805 class MultiDict(DictMixin):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1806 """ This dict stores multiple values per key, but behaves exactly like a
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1807 normal dict in that it returns only the newest value for any given key.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1808 There are special methods available to access the full list of values.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1809 """
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1810
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1811 def __init__(self, *a, **k):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1812 self.dict = dict((k, [v]) for (k, v) in dict(*a, **k).items())
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1813
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1814 def __len__(self): return len(self.dict)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1815 def __iter__(self): return iter(self.dict)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1816 def __contains__(self, key): return key in self.dict
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1817 def __delitem__(self, key): del self.dict[key]
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1818 def __getitem__(self, key): return self.dict[key][-1]
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1819 def __setitem__(self, key, value): self.append(key, value)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1820 def keys(self): return self.dict.keys()
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1821
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1822 if py3k:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1823 def values(self): return (v[-1] for v in self.dict.values())
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1824 def items(self): return ((k, v[-1]) for k, v in self.dict.items())
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1825 def allitems(self):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1826 return ((k, v) for k, vl in self.dict.items() for v in vl)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1827 iterkeys = keys
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1828 itervalues = values
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1829 iteritems = items
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1830 iterallitems = allitems
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1831
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1832 else:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1833 def values(self): return [v[-1] for v in self.dict.values()]
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1834 def items(self): return [(k, v[-1]) for k, v in self.dict.items()]
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1835 def iterkeys(self): return self.dict.iterkeys()
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1836 def itervalues(self): return (v[-1] for v in self.dict.itervalues())
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1837 def iteritems(self):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1838 return ((k, v[-1]) for k, v in self.dict.iteritems())
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1839 def iterallitems(self):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1840 return ((k, v) for k, vl in self.dict.iteritems() for v in vl)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1841 def allitems(self):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1842 return [(k, v) for k, vl in self.dict.iteritems() for v in vl]
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1843
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1844 def get(self, key, default=None, index=-1, type=None):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1845 ''' Return the most recent value for a key.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1846
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1847 :param default: The default value to be returned if the key is not
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1848 present or the type conversion fails.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1849 :param index: An index for the list of available values.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1850 :param type: If defined, this callable is used to cast the value
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1851 into a specific type. Exception are suppressed and result in
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1852 the default value to be returned.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1853 '''
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1854 try:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1855 val = self.dict[key][index]
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1856 return type(val) if type else val
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1857 except Exception:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1858 pass
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1859 return default
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1860
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1861 def append(self, key, value):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1862 ''' Add a new value to the list of values for this key. '''
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1863 self.dict.setdefault(key, []).append(value)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1864
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1865 def replace(self, key, value):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1866 ''' Replace the list of values with a single value. '''
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1867 self.dict[key] = [value]
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1868
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1869 def getall(self, key):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1870 ''' Return a (possibly empty) list of values for a key. '''
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1871 return self.dict.get(key) or []
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1872
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1873 #: Aliases for WTForms to mimic other multi-dict APIs (Django)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1874 getone = get
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1875 getlist = getall
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1876
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1877
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1878 class FormsDict(MultiDict):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1879 ''' This :class:`MultiDict` subclass is used to store request form data.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1880 Additionally to the normal dict-like item access methods (which return
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1881 unmodified data as native strings), this container also supports
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1882 attribute-like access to its values. Attributes are automatically de-
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1883 or recoded to match :attr:`input_encoding` (default: 'utf8'). Missing
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1884 attributes default to an empty string. '''
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1885
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1886 #: Encoding used for attribute values.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1887 input_encoding = 'utf8'
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1888 #: If true (default), unicode strings are first encoded with `latin1`
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1889 #: and then decoded to match :attr:`input_encoding`.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1890 recode_unicode = True
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1891
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1892 def _fix(self, s, encoding=None):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1893 if isinstance(s, unicode) and self.recode_unicode: # Python 3 WSGI
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1894 return s.encode('latin1').decode(encoding or self.input_encoding)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1895 elif isinstance(s, bytes): # Python 2 WSGI
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1896 return s.decode(encoding or self.input_encoding)
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1897 else:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1898 return s
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1899
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1900 def decode(self, encoding=None):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1901 ''' Returns a copy with all keys and values de- or recoded to match
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1902 :attr:`input_encoding`. Some libraries (e.g. WTForms) want a
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1903 unicode dictionary. '''
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1904 copy = FormsDict()
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1905 enc = copy.input_encoding = encoding or self.input_encoding
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1906 copy.recode_unicode = False
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1907 for key, value in self.allitems():
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1908 copy.append(self._fix(key, enc), self._fix(value, enc))
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1909 return copy
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1910
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1911 def getunicode(self, name, default=None, encoding=None):
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1912 ''' Return the value as a unicode string, or the default. '''
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1913 try:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1914 return self._fix(self[name], encoding)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1915 except (UnicodeError, KeyError):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1916 return default
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1917
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1918 def __getattr__(self, name, default=unicode()):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1919 # Without this guard, pickle generates a cryptic TypeError:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1920 if name.startswith('__') and name.endswith('__'):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1921 return super(FormsDict, self).__getattr__(name)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1922 return self.getunicode(name, default=default)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1923
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1924
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1925 class HeaderDict(MultiDict):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1926 """ A case-insensitive version of :class:`MultiDict` that defaults to
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1927 replace the old value instead of appending it. """
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1928
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1929 def __init__(self, *a, **ka):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1930 self.dict = {}
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1931 if a or ka: self.update(*a, **ka)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1932
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1933 def __contains__(self, key): return _hkey(key) in self.dict
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1934 def __delitem__(self, key): del self.dict[_hkey(key)]
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1935 def __getitem__(self, key): return self.dict[_hkey(key)][-1]
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1936 def __setitem__(self, key, value): self.dict[_hkey(key)] = [str(value)]
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1937 def append(self, key, value):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1938 self.dict.setdefault(_hkey(key), []).append(str(value))
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1939 def replace(self, key, value): self.dict[_hkey(key)] = [str(value)]
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1940 def getall(self, key): return self.dict.get(_hkey(key)) or []
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1941 def get(self, key, default=None, index=-1):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1942 return MultiDict.get(self, _hkey(key), default, index)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1943 def filter(self, names):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1944 for name in [_hkey(n) for n in names]:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1945 if name in self.dict:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1946 del self.dict[name]
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1947
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1948
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1949 class WSGIHeaderDict(DictMixin):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1950 ''' This dict-like class wraps a WSGI environ dict and provides convenient
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1951 access to HTTP_* fields. Keys and values are native strings
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1952 (2.x bytes or 3.x unicode) and keys are case-insensitive. If the WSGI
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1953 environment contains non-native string values, these are de- or encoded
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1954 using a lossless 'latin1' character set.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1955
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1956 The API will remain stable even on changes to the relevant PEPs.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1957 Currently PEP 333, 444 and 3333 are supported. (PEP 444 is the only one
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1958 that uses non-native strings.)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1959 '''
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1960 #: List of keys that do not have a ``HTTP_`` prefix.
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1961 cgikeys = ('CONTENT_TYPE', 'CONTENT_LENGTH')
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1962
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1963 def __init__(self, environ):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1964 self.environ = environ
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1965
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1966 def _ekey(self, key):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1967 ''' Translate header field name to CGI/WSGI environ key. '''
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1968 key = key.replace('-','_').upper()
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1969 if key in self.cgikeys:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1970 return key
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1971 return 'HTTP_' + key
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1972
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1973 def raw(self, key, default=None):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1974 ''' Return the header value as is (may be bytes or unicode). '''
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1975 return self.environ.get(self._ekey(key), default)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1976
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1977 def __getitem__(self, key):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1978 return tonat(self.environ[self._ekey(key)], 'latin1')
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1979
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1980 def __setitem__(self, key, value):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1981 raise TypeError("%s is read-only." % self.__class__)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1982
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1983 def __delitem__(self, key):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1984 raise TypeError("%s is read-only." % self.__class__)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1985
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1986 def __iter__(self):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1987 for key in self.environ:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1988 if key[:5] == 'HTTP_':
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1989 yield key[5:].replace('_', '-').title()
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1990 elif key in self.cgikeys:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1991 yield key.replace('_', '-').title()
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1992
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1993 def keys(self): return [x for x in self]
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1994 def __len__(self): return len(self.keys())
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1995 def __contains__(self, key): return self._ekey(key) in self.environ
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1996
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1997
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
1998
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1999 class ConfigDict(dict):
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2000 ''' A dict-like configuration storage with additional support for
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2001 namespaces, validators, meta-data, on_change listeners and more.
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2002
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2003 This storage is optimized for fast read access. Retrieving a key
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2004 or using non-altering dict methods (e.g. `dict.get()`) has no overhead
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2005 compared to a native dict.
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2006 '''
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2007 __slots__ = ('_meta', '_on_change')
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2008
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2009 class Namespace(DictMixin):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2010
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2011 def __init__(self, config, namespace):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2012 self._config = config
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2013 self._prefix = namespace
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2014
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2015 def __getitem__(self, key):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2016 depr('Accessing namespaces as dicts is discouraged. '
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2017 'Only use flat item access: '
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2018 'cfg["names"]["pace"]["key"] -> cfg["name.space.key"]') #0.12
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2019 return self._config[self._prefix + '.' + key]
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2020
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2021 def __setitem__(self, key, value):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2022 self._config[self._prefix + '.' + key] = value
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2023
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2024 def __delitem__(self, key):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2025 del self._config[self._prefix + '.' + key]
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2026
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2027 def __iter__(self):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2028 ns_prefix = self._prefix + '.'
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2029 for key in self._config:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2030 ns, dot, name = key.rpartition('.')
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2031 if ns == self._prefix and name:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2032 yield name
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2033
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2034 def keys(self): return [x for x in self]
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2035 def __len__(self): return len(self.keys())
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2036 def __contains__(self, key): return self._prefix + '.' + key in self._config
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2037 def __repr__(self): return '<Config.Namespace %s.*>' % self._prefix
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2038 def __str__(self): return '<Config.Namespace %s.*>' % self._prefix
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2039
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2040 # Deprecated ConfigDict features
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2041 def __getattr__(self, key):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2042 depr('Attribute access is deprecated.') #0.12
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2043 if key not in self and key[0].isupper():
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2044 self[key] = ConfigDict.Namespace(self._config, self._prefix + '.' + key)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2045 if key not in self and key.startswith('__'):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2046 raise AttributeError(key)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2047 return self.get(key)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2048
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2049 def __setattr__(self, key, value):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2050 if key in ('_config', '_prefix'):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2051 self.__dict__[key] = value
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2052 return
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2053 depr('Attribute assignment is deprecated.') #0.12
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2054 if hasattr(DictMixin, key):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2055 raise AttributeError('Read-only attribute.')
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2056 if key in self and self[key] and isinstance(self[key], self.__class__):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2057 raise AttributeError('Non-empty namespace attribute.')
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2058 self[key] = value
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2059
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2060 def __delattr__(self, key):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2061 if key in self:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2062 val = self.pop(key)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2063 if isinstance(val, self.__class__):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2064 prefix = key + '.'
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2065 for key in self:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2066 if key.startswith(prefix):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2067 del self[prefix+key]
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2068
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2069 def __call__(self, *a, **ka):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2070 depr('Calling ConfDict is deprecated. Use the update() method.') #0.12
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2071 self.update(*a, **ka)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2072 return self
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2073
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2074 def __init__(self, *a, **ka):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2075 self._meta = {}
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2076 self._on_change = lambda name, value: None
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2077 if a or ka:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2078 depr('Constructor does no longer accept parameters.') #0.12
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2079 self.update(*a, **ka)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2080
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2081 def load_config(self, filename):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2082 ''' Load values from an *.ini style config file.
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2083
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2084 If the config file contains sections, their names are used as
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2085 namespaces for the values within. The two special sections
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2086 ``DEFAULT`` and ``bottle`` refer to the root namespace (no prefix).
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2087 '''
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2088 conf = ConfigParser()
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2089 conf.read(filename)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2090 for section in conf.sections():
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2091 for key, value in conf.items(section):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2092 if section not in ('DEFAULT', 'bottle'):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2093 key = section + '.' + key
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2094 self[key] = value
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2095 return self
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2096
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2097 def load_dict(self, source, namespace='', make_namespaces=False):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2098 ''' Import values from a dictionary structure. Nesting can be used to
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2099 represent namespaces.
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2100
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2101 >>> ConfigDict().load_dict({'name': {'space': {'key': 'value'}}})
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2102 {'name.space.key': 'value'}
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2103 '''
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2104 stack = [(namespace, source)]
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2105 while stack:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2106 prefix, source = stack.pop()
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2107 if not isinstance(source, dict):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2108 raise TypeError('Source is not a dict (r)' % type(key))
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2109 for key, value in source.items():
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2110 if not isinstance(key, str):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2111 raise TypeError('Key is not a string (%r)' % type(key))
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2112 full_key = prefix + '.' + key if prefix else key
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2113 if isinstance(value, dict):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2114 stack.append((full_key, value))
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2115 if make_namespaces:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2116 self[full_key] = self.Namespace(self, full_key)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2117 else:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2118 self[full_key] = value
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2119 return self
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2120
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2121 def update(self, *a, **ka):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2122 ''' If the first parameter is a string, all keys are prefixed with this
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2123 namespace. Apart from that it works just as the usual dict.update().
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2124 Example: ``update('some.namespace', key='value')`` '''
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2125 prefix = ''
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2126 if a and isinstance(a[0], str):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2127 prefix = a[0].strip('.') + '.'
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2128 a = a[1:]
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2129 for key, value in dict(*a, **ka).items():
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2130 self[prefix+key] = value
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2131
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2132 def setdefault(self, key, value):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2133 if key not in self:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2134 self[key] = value
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2135 return self[key]
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2136
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2137 def __setitem__(self, key, value):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2138 if not isinstance(key, str):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2139 raise TypeError('Key has type %r (not a string)' % type(key))
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2140
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2141 value = self.meta_get(key, 'filter', lambda x: x)(value)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2142 if key in self and self[key] is value:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2143 return
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2144 self._on_change(key, value)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2145 dict.__setitem__(self, key, value)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2146
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2147 def __delitem__(self, key):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2148 dict.__delitem__(self, key)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2149
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2150 def clear(self):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2151 for key in self:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2152 del self[key]
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2153
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2154 def meta_get(self, key, metafield, default=None):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2155 ''' Return the value of a meta field for a key. '''
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2156 return self._meta.get(key, {}).get(metafield, default)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2157
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2158 def meta_set(self, key, metafield, value):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2159 ''' Set the meta field for a key to a new value. This triggers the
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2160 on-change handler for existing keys. '''
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2161 self._meta.setdefault(key, {})[metafield] = value
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2162 if key in self:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2163 self[key] = self[key]
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2164
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2165 def meta_list(self, key):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2166 ''' Return an iterable of meta field names defined for a key. '''
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2167 return self._meta.get(key, {}).keys()
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2168
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2169 # Deprecated ConfigDict features
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2170 def __getattr__(self, key):
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2171 depr('Attribute access is deprecated.') #0.12
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2172 if key not in self and key[0].isupper():
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2173 self[key] = self.Namespace(self, key)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2174 if key not in self and key.startswith('__'):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2175 raise AttributeError(key)
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2176 return self.get(key)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2177
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2178 def __setattr__(self, key, value):
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2179 if key in self.__slots__:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2180 return dict.__setattr__(self, key, value)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2181 depr('Attribute assignment is deprecated.') #0.12
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2182 if hasattr(dict, key):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2183 raise AttributeError('Read-only attribute.')
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2184 if key in self and self[key] and isinstance(self[key], self.Namespace):
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2185 raise AttributeError('Non-empty namespace attribute.')
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2186 self[key] = value
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2187
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2188 def __delattr__(self, key):
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2189 if key in self:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2190 val = self.pop(key)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2191 if isinstance(val, self.Namespace):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2192 prefix = key + '.'
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2193 for key in self:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2194 if key.startswith(prefix):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2195 del self[prefix+key]
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2196
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2197 def __call__(self, *a, **ka):
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2198 depr('Calling ConfDict is deprecated. Use the update() method.') #0.12
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2199 self.update(*a, **ka)
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2200 return self
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2201
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2202
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2203
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2204 class AppStack(list):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2205 """ A stack-like list. Calling it returns the head of the stack. """
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2206
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2207 def __call__(self):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2208 """ Return the current default application. """
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2209 return self[-1]
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2210
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2211 def push(self, value=None):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2212 """ Add a new :class:`Bottle` instance to the stack """
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2213 if not isinstance(value, Bottle):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2214 value = Bottle()
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2215 self.append(value)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2216 return value
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2217
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2218
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2219 class WSGIFileWrapper(object):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2220
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2221 def __init__(self, fp, buffer_size=1024*64):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2222 self.fp, self.buffer_size = fp, buffer_size
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2223 for attr in ('fileno', 'close', 'read', 'readlines', 'tell', 'seek'):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2224 if hasattr(fp, attr): setattr(self, attr, getattr(fp, attr))
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2225
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2226 def __iter__(self):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2227 buff, read = self.buffer_size, self.read
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2228 while True:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2229 part = read(buff)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2230 if not part: return
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2231 yield part
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2232
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2233
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2234 class _closeiter(object):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2235 ''' This only exists to be able to attach a .close method to iterators that
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2236 do not support attribute assignment (most of itertools). '''
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2237
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2238 def __init__(self, iterator, close=None):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2239 self.iterator = iterator
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2240 self.close_callbacks = makelist(close)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2241
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2242 def __iter__(self):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2243 return iter(self.iterator)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2244
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2245 def close(self):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2246 for func in self.close_callbacks:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2247 func()
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2249
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2250 class ResourceManager(object):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2251 ''' This class manages a list of search paths and helps to find and open
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2252 application-bound resources (files).
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2253
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2254 :param base: default value for :meth:`add_path` calls.
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2255 :param opener: callable used to open resources.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2256 :param cachemode: controls which lookups are cached. One of 'all',
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2257 'found' or 'none'.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2258 '''
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2259
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2260 def __init__(self, base='./', opener=open, cachemode='all'):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2261 self.opener = open
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2262 self.base = base
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2263 self.cachemode = cachemode
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2264
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2265 #: A list of search paths. See :meth:`add_path` for details.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2266 self.path = []
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2267 #: A cache for resolved paths. ``res.cache.clear()`` clears the cache.
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2268 self.cache = {}
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2269
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2270 def add_path(self, path, base=None, index=None, create=False):
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2271 ''' Add a new path to the list of search paths. Return False if the
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2272 path does not exist.
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2273
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2274 :param path: The new search path. Relative paths are turned into
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2275 an absolute and normalized form. If the path looks like a file
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2276 (not ending in `/`), the filename is stripped off.
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2277 :param base: Path used to absolutize relative search paths.
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2278 Defaults to :attr:`base` which defaults to ``os.getcwd()``.
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2279 :param index: Position within the list of search paths. Defaults
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2280 to last index (appends to the list).
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2281
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2282 The `base` parameter makes it easy to reference files installed
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2283 along with a python module or package::
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2284
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2285 res.add_path('./resources/', __file__)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2286 '''
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2287 base = os.path.abspath(os.path.dirname(base or self.base))
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2288 path = os.path.abspath(os.path.join(base, os.path.dirname(path)))
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2289 path += os.sep
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2290 if path in self.path:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2291 self.path.remove(path)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2292 if create and not os.path.isdir(path):
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2293 os.makedirs(path)
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2294 if index is None:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2295 self.path.append(path)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2296 else:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2297 self.path.insert(index, path)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2298 self.cache.clear()
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2299 return os.path.exists(path)
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2300
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2301 def __iter__(self):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2302 ''' Iterate over all existing files in all registered paths. '''
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2303 search = self.path[:]
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2304 while search:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2305 path = search.pop()
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2306 if not os.path.isdir(path): continue
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2307 for name in os.listdir(path):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2308 full = os.path.join(path, name)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2309 if os.path.isdir(full): search.append(full)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2310 else: yield full
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2311
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2312 def lookup(self, name):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2313 ''' Search for a resource and return an absolute file path, or `None`.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2314
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2315 The :attr:`path` list is searched in order. The first match is
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2316 returend. Symlinks are followed. The result is cached to speed up
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2317 future lookups. '''
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2318 if name not in self.cache or DEBUG:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2319 for path in self.path:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2320 fpath = os.path.join(path, name)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2321 if os.path.isfile(fpath):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2322 if self.cachemode in ('all', 'found'):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2323 self.cache[name] = fpath
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2324 return fpath
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2325 if self.cachemode == 'all':
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2326 self.cache[name] = None
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2327 return self.cache[name]
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2328
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2329 def open(self, name, mode='r', *args, **kwargs):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2330 ''' Find a resource and return a file object, or raise IOError. '''
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2331 fname = self.lookup(name)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2332 if not fname: raise IOError("Resource %r not found." % name)
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2333 return self.opener(fname, mode=mode, *args, **kwargs)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2334
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2335
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2336 class FileUpload(object):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2337
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2338 def __init__(self, fileobj, name, filename, headers=None):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2339 ''' Wrapper for file uploads. '''
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2340 #: Open file(-like) object (BytesIO buffer or temporary file)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2341 self.file = fileobj
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2342 #: Name of the upload form field
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2343 self.name = name
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2344 #: Raw filename as sent by the client (may contain unsafe characters)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2345 self.raw_filename = filename
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2346 #: A :class:`HeaderDict` with additional headers (e.g. content-type)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2347 self.headers = HeaderDict(headers) if headers else HeaderDict()
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2348
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2349 content_type = HeaderProperty('Content-Type')
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2350 content_length = HeaderProperty('Content-Length', reader=int, default=-1)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2351
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2352 @cached_property
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2353 def filename(self):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2354 ''' Name of the file on the client file system, but normalized to ensure
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2355 file system compatibility. An empty filename is returned as 'empty'.
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2356
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2357 Only ASCII letters, digits, dashes, underscores and dots are
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2358 allowed in the final filename. Accents are removed, if possible.
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2359 Whitespace is replaced by a single dash. Leading or tailing dots
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2360 or dashes are removed. The filename is limited to 255 characters.
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2361 '''
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2362 fname = self.raw_filename
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2363 if not isinstance(fname, unicode):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2364 fname = fname.decode('utf8', 'ignore')
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2365 fname = normalize('NFKD', fname).encode('ASCII', 'ignore').decode('ASCII')
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2366 fname = os.path.basename(fname.replace('\\', os.path.sep))
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2367 fname = re.sub(r'[^a-zA-Z0-9-_.\s]', '', fname).strip()
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2368 fname = re.sub(r'[-\s]+', '-', fname).strip('.-')
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2369 return fname[:255] or 'empty'
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2370
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2371 def _copy_file(self, fp, chunk_size=2**16):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2372 read, write, offset = self.file.read, fp.write, self.file.tell()
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2373 while 1:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2374 buf = read(chunk_size)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2375 if not buf: break
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2376 write(buf)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2377 self.file.seek(offset)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2378
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2379 def save(self, destination, overwrite=False, chunk_size=2**16):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2380 ''' Save file to disk or copy its content to an open file(-like) object.
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2381 If *destination* is a directory, :attr:`filename` is added to the
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2382 path. Existing files are not overwritten by default (IOError).
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2383
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2384 :param destination: File path, directory or file(-like) object.
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2385 :param overwrite: If True, replace existing files. (default: False)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2386 :param chunk_size: Bytes to read at a time. (default: 64kb)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2387 '''
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2388 if isinstance(destination, basestring): # Except file-likes here
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2389 if os.path.isdir(destination):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2390 destination = os.path.join(destination, self.filename)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2391 if not overwrite and os.path.exists(destination):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2392 raise IOError('File exists.')
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2393 with open(destination, 'wb') as fp:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2394 self._copy_file(fp, chunk_size)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2395 else:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2396 self._copy_file(destination, chunk_size)
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2397
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2398
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2399
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2400
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2401
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2402
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2403 ###############################################################################
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2404 # Application Helper ###########################################################
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2405 ###############################################################################
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2406
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2407
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2408 def abort(code=500, text='Unknown Error.'):
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2409 """ Aborts execution and causes a HTTP error. """
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2410 raise HTTPError(code, text)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2411
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2412
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2413 def redirect(url, code=None):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2414 """ Aborts execution and causes a 303 or 302 redirect, depending on
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2415 the HTTP protocol version. """
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2416 if not code:
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2417 code = 303 if request.get('SERVER_PROTOCOL') == "HTTP/1.1" else 302
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2418 res = response.copy(cls=HTTPResponse)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2419 res.status = code
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2420 res.body = ""
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2421 res.set_header('Location', urljoin(request.url, url))
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2422 raise res
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2423
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2424
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2425 def _file_iter_range(fp, offset, bytes, maxread=1024*1024):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2426 ''' Yield chunks from a range in a file. No chunk is bigger than maxread.'''
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2427 fp.seek(offset)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2428 while bytes > 0:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2429 part = fp.read(min(bytes, maxread))
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2430 if not part: break
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2431 bytes -= len(part)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2432 yield part
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2433
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2434
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2435 def static_file(filename, root, mimetype='auto', download=False, charset='UTF-8'):
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2436 """ Open a file in a safe way and return :exc:`HTTPResponse` with status
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2437 code 200, 305, 403 or 404. The ``Content-Type``, ``Content-Encoding``,
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2438 ``Content-Length`` and ``Last-Modified`` headers are set if possible.
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2439 Special support for ``If-Modified-Since``, ``Range`` and ``HEAD``
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2440 requests.
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2441
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2442 :param filename: Name or path of the file to send.
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2443 :param root: Root path for file lookups. Should be an absolute directory
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2444 path.
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2445 :param mimetype: Defines the content-type header (default: guess from
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2446 file extension)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2447 :param download: If True, ask the browser to open a `Save as...` dialog
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2448 instead of opening the file with the associated program. You can
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2449 specify a custom filename as a string. If not specified, the
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2450 original filename is used (default: False).
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2451 :param charset: The charset to use for files with a ``text/*``
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2452 mime-type. (default: UTF-8)
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2453 """
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2454
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2455 root = os.path.abspath(root) + os.sep
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2456 filename = os.path.abspath(os.path.join(root, filename.strip('/\\')))
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2457 headers = dict()
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2458
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2459 if not filename.startswith(root):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2460 return HTTPError(403, "Access denied.")
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2461 if not os.path.exists(filename) or not os.path.isfile(filename):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2462 return HTTPError(404, "File does not exist.")
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2463 if not os.access(filename, os.R_OK):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2464 return HTTPError(403, "You do not have permission to access this file.")
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2465
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2466 if mimetype == 'auto':
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2467 mimetype, encoding = mimetypes.guess_type(filename)
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2468 if encoding: headers['Content-Encoding'] = encoding
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2469
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2470 if mimetype:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2471 if mimetype[:5] == 'text/' and charset and 'charset' not in mimetype:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2472 mimetype += '; charset=%s' % charset
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2473 headers['Content-Type'] = mimetype
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2474
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2475 if download:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2476 download = os.path.basename(filename if download == True else download)
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2477 headers['Content-Disposition'] = 'attachment; filename="%s"' % download
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2478
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2479 stats = os.stat(filename)
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2480 headers['Content-Length'] = clen = stats.st_size
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2481 lm = time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(stats.st_mtime))
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2482 headers['Last-Modified'] = lm
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2483
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2484 ims = request.environ.get('HTTP_IF_MODIFIED_SINCE')
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2485 if ims:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2486 ims = parse_date(ims.split(";")[0].strip())
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2487 if ims is not None and ims >= int(stats.st_mtime):
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2488 headers['Date'] = time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime())
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2489 return HTTPResponse(status=304, **headers)
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2490
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2491 body = '' if request.method == 'HEAD' else open(filename, 'rb')
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2492
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2493 headers["Accept-Ranges"] = "bytes"
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2494 ranges = request.environ.get('HTTP_RANGE')
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2495 if 'HTTP_RANGE' in request.environ:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2496 ranges = list(parse_range_header(request.environ['HTTP_RANGE'], clen))
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2497 if not ranges:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2498 return HTTPError(416, "Requested Range Not Satisfiable")
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2499 offset, end = ranges[0]
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2500 headers["Content-Range"] = "bytes %d-%d/%d" % (offset, end-1, clen)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2501 headers["Content-Length"] = str(end-offset)
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2502 if body: body = _file_iter_range(body, offset, end-offset)
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2503 return HTTPResponse(body, status=206, **headers)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2504 return HTTPResponse(body, **headers)
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2505
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2506
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2507
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2508
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2509
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2510
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2511 ###############################################################################
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2512 # HTTP Utilities and MISC (TODO) ###############################################
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2513 ###############################################################################
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2514
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2515
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2516 def debug(mode=True):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2517 """ Change the debug level.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2518 There is only one debug level supported at the moment."""
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2519 global DEBUG
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2520 if mode: warnings.simplefilter('default')
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2521 DEBUG = bool(mode)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2522
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2523 def http_date(value):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2524 if isinstance(value, (datedate, datetime)):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2525 value = value.utctimetuple()
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2526 elif isinstance(value, (int, float)):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2527 value = time.gmtime(value)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2528 if not isinstance(value, basestring):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2529 value = time.strftime("%a, %d %b %Y %H:%M:%S GMT", value)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2530 return value
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2531
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2532 def parse_date(ims):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2533 """ Parse rfc1123, rfc850 and asctime timestamps and return UTC epoch. """
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2534 try:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2535 ts = email.utils.parsedate_tz(ims)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2536 return time.mktime(ts[:8] + (0,)) - (ts[9] or 0) - time.timezone
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2537 except (TypeError, ValueError, IndexError, OverflowError):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2538 return None
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2539
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2540 def parse_auth(header):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2541 """ Parse rfc2617 HTTP authentication header string (basic) and return (user,pass) tuple or None"""
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2542 try:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2543 method, data = header.split(None, 1)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2544 if method.lower() == 'basic':
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2545 user, pwd = touni(base64.b64decode(tob(data))).split(':',1)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2546 return user, pwd
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2547 except (KeyError, ValueError):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2548 return None
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2549
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2550 def parse_range_header(header, maxlen=0):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2551 ''' Yield (start, end) ranges parsed from a HTTP Range header. Skip
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2552 unsatisfiable ranges. The end index is non-inclusive.'''
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2553 if not header or header[:6] != 'bytes=': return
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2554 ranges = [r.split('-', 1) for r in header[6:].split(',') if '-' in r]
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2555 for start, end in ranges:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2556 try:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2557 if not start: # bytes=-100 -> last 100 bytes
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2558 start, end = max(0, maxlen-int(end)), maxlen
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2559 elif not end: # bytes=100- -> all but the first 99 bytes
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2560 start, end = int(start), maxlen
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2561 else: # bytes=100-200 -> bytes 100-200 (inclusive)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2562 start, end = int(start), min(int(end)+1, maxlen)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2563 if 0 <= start < end <= maxlen:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2564 yield start, end
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2565 except ValueError:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2566 pass
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2567
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2568 def _parse_qsl(qs):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2569 r = []
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2570 for pair in qs.replace(';','&').split('&'):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2571 if not pair: continue
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2572 nv = pair.split('=', 1)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2573 if len(nv) != 2: nv.append('')
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2574 key = urlunquote(nv[0].replace('+', ' '))
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2575 value = urlunquote(nv[1].replace('+', ' '))
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2576 r.append((key, value))
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2577 return r
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2578
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2579 def _lscmp(a, b):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2580 ''' Compares two strings in a cryptographically safe way:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2581 Runtime is not affected by length of common prefix. '''
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2582 return not sum(0 if x==y else 1 for x, y in zip(a, b)) and len(a) == len(b)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2583
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2584
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2585 def cookie_encode(data, key):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2586 ''' Encode and sign a pickle-able object. Return a (byte) string '''
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2587 msg = base64.b64encode(pickle.dumps(data, -1))
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2588 sig = base64.b64encode(hmac.new(tob(key), msg).digest())
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2589 return tob('!') + sig + tob('?') + msg
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2590
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2591
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2592 def cookie_decode(data, key):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2593 ''' Verify and decode an encoded string. Return an object or None.'''
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2594 data = tob(data)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2595 if cookie_is_encoded(data):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2596 sig, msg = data.split(tob('?'), 1)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2597 if _lscmp(sig[1:], base64.b64encode(hmac.new(tob(key), msg).digest())):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2598 return pickle.loads(base64.b64decode(msg))
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2599 return None
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2600
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2601
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2602 def cookie_is_encoded(data):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2603 ''' Return True if the argument looks like a encoded cookie.'''
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2604 return bool(data.startswith(tob('!')) and tob('?') in data)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2605
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2606
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2607 def html_escape(string):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2608 ''' Escape HTML special characters ``&<>`` and quotes ``'"``. '''
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2609 return string.replace('&','&amp;').replace('<','&lt;').replace('>','&gt;')\
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2610 .replace('"','&quot;').replace("'",'&#039;')
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2611
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2612
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2613 def html_quote(string):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2614 ''' Escape and quote a string to be used as an HTTP attribute.'''
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2615 return '"%s"' % html_escape(string).replace('\n','&#10;')\
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2616 .replace('\r','&#13;').replace('\t','&#9;')
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2617
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2618
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2619 def yieldroutes(func):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2620 """ Return a generator for routes that match the signature (name, args)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2621 of the func parameter. This may yield more than one route if the function
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2622 takes optional keyword arguments. The output is best described by example::
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2623
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2624 a() -> '/a'
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2625 b(x, y) -> '/b/<x>/<y>'
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2626 c(x, y=5) -> '/c/<x>' and '/c/<x>/<y>'
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2627 d(x=5, y=6) -> '/d' and '/d/<x>' and '/d/<x>/<y>'
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2628 """
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2629 path = '/' + func.__name__.replace('__','/').lstrip('/')
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2630 spec = getargspec(func)
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2631 argc = len(spec[0]) - len(spec[3] or [])
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2632 path += ('/<%s>' * argc) % tuple(spec[0][:argc])
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2633 yield path
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2634 for arg in spec[0][argc:]:
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2635 path += '/<%s>' % arg
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2636 yield path
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2637
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2638
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2639 def path_shift(script_name, path_info, shift=1):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2640 ''' Shift path fragments from PATH_INFO to SCRIPT_NAME and vice versa.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2641
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2642 :return: The modified paths.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2643 :param script_name: The SCRIPT_NAME path.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2644 :param script_name: The PATH_INFO path.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2645 :param shift: The number of path fragments to shift. May be negative to
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2646 change the shift direction. (default: 1)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2647 '''
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2648 if shift == 0: return script_name, path_info
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2649 pathlist = path_info.strip('/').split('/')
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2650 scriptlist = script_name.strip('/').split('/')
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2651 if pathlist and pathlist[0] == '': pathlist = []
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2652 if scriptlist and scriptlist[0] == '': scriptlist = []
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2653 if shift > 0 and shift <= len(pathlist):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2654 moved = pathlist[:shift]
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2655 scriptlist = scriptlist + moved
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2656 pathlist = pathlist[shift:]
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2657 elif shift < 0 and shift >= -len(scriptlist):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2658 moved = scriptlist[shift:]
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2659 pathlist = moved + pathlist
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2660 scriptlist = scriptlist[:shift]
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2661 else:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2662 empty = 'SCRIPT_NAME' if shift < 0 else 'PATH_INFO'
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2663 raise AssertionError("Cannot shift. Nothing left from %s" % empty)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2664 new_script_name = '/' + '/'.join(scriptlist)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2665 new_path_info = '/' + '/'.join(pathlist)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2666 if path_info.endswith('/') and pathlist: new_path_info += '/'
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2667 return new_script_name, new_path_info
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2668
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2669
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2670 def auth_basic(check, realm="private", text="Access denied"):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2671 ''' Callback decorator to require HTTP auth (basic).
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2672 TODO: Add route(check_auth=...) parameter. '''
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2673 def decorator(func):
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2674 def wrapper(*a, **ka):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2675 user, password = request.auth or (None, None)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2676 if user is None or not check(user, password):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2677 err = HTTPError(401, text)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2678 err.add_header('WWW-Authenticate', 'Basic realm="%s"' % realm)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2679 return err
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2680 return func(*a, **ka)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2681 return wrapper
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2682 return decorator
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2683
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2684
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2685 # Shortcuts for common Bottle methods.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2686 # They all refer to the current default application.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2687
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2688 def make_default_app_wrapper(name):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2689 ''' Return a callable that relays calls to the current default app. '''
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2690 @functools.wraps(getattr(Bottle, name))
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2691 def wrapper(*a, **ka):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2692 return getattr(app(), name)(*a, **ka)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2693 return wrapper
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2694
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2695 route = make_default_app_wrapper('route')
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2696 get = make_default_app_wrapper('get')
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2697 post = make_default_app_wrapper('post')
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2698 put = make_default_app_wrapper('put')
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2699 delete = make_default_app_wrapper('delete')
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2700 error = make_default_app_wrapper('error')
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2701 mount = make_default_app_wrapper('mount')
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2702 hook = make_default_app_wrapper('hook')
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2703 install = make_default_app_wrapper('install')
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2704 uninstall = make_default_app_wrapper('uninstall')
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2705 url = make_default_app_wrapper('get_url')
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2706
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2707
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2708
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2709
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2710
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2711
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2712
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2713 ###############################################################################
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2714 # Server Adapter ###############################################################
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2715 ###############################################################################
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2716
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2717
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2718 class ServerAdapter(object):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2719 quiet = False
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2720 def __init__(self, host='127.0.0.1', port=8080, **options):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2721 self.options = options
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2722 self.host = host
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2723 self.port = int(port)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2724
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2725 def run(self, handler): # pragma: no cover
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2726 pass
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2727
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2728 def __repr__(self):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2729 args = ', '.join(['%s=%s'%(k,repr(v)) for k, v in self.options.items()])
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2730 return "%s(%s)" % (self.__class__.__name__, args)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2731
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2732
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2733 class CGIServer(ServerAdapter):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2734 quiet = True
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2735 def run(self, handler): # pragma: no cover
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2736 from wsgiref.handlers import CGIHandler
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2737 def fixed_environ(environ, start_response):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2738 environ.setdefault('PATH_INFO', '')
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2739 return handler(environ, start_response)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2740 CGIHandler().run(fixed_environ)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2741
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2742
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2743 class FlupFCGIServer(ServerAdapter):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2744 def run(self, handler): # pragma: no cover
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2745 import flup.server.fcgi
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2746 self.options.setdefault('bindAddress', (self.host, self.port))
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2747 flup.server.fcgi.WSGIServer(handler, **self.options).run()
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2748
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2749
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2750 class WSGIRefServer(ServerAdapter):
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2751 def run(self, app): # pragma: no cover
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2752 from wsgiref.simple_server import WSGIRequestHandler, WSGIServer
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2753 from wsgiref.simple_server import make_server
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2754 import socket
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2755
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2756 class FixedHandler(WSGIRequestHandler):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2757 def address_string(self): # Prevent reverse DNS lookups please.
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2758 return self.client_address[0]
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2759 def log_request(*args, **kw):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2760 if not self.quiet:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2761 return WSGIRequestHandler.log_request(*args, **kw)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2762
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2763 handler_cls = self.options.get('handler_class', FixedHandler)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2764 server_cls = self.options.get('server_class', WSGIServer)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2765
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2766 if ':' in self.host: # Fix wsgiref for IPv6 addresses.
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2767 if getattr(server_cls, 'address_family') == socket.AF_INET:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2768 class server_cls(server_cls):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2769 address_family = socket.AF_INET6
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2770
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2771 srv = make_server(self.host, self.port, app, server_cls, handler_cls)
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2772 srv.serve_forever()
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2773
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2774
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2775 class CherryPyServer(ServerAdapter):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2776 def run(self, handler): # pragma: no cover
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2777 from cherrypy import wsgiserver
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2778 self.options['bind_addr'] = (self.host, self.port)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2779 self.options['wsgi_app'] = handler
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2780
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2781 certfile = self.options.get('certfile')
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2782 if certfile:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2783 del self.options['certfile']
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2784 keyfile = self.options.get('keyfile')
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2785 if keyfile:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2786 del self.options['keyfile']
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2787
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2788 server = wsgiserver.CherryPyWSGIServer(**self.options)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2789 if certfile:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2790 server.ssl_certificate = certfile
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2791 if keyfile:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2792 server.ssl_private_key = keyfile
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2793
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2794 try:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2795 server.start()
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2796 finally:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2797 server.stop()
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2798
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2799
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2800 class WaitressServer(ServerAdapter):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2801 def run(self, handler):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2802 from waitress import serve
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2803 serve(handler, host=self.host, port=self.port)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2804
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2805
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2806 class PasteServer(ServerAdapter):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2807 def run(self, handler): # pragma: no cover
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2808 from paste import httpserver
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2809 from paste.translogger import TransLogger
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2810 handler = TransLogger(handler, setup_console_handler=(not self.quiet))
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2811 httpserver.serve(handler, host=self.host, port=str(self.port),
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2812 **self.options)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2813
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2814
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2815 class MeinheldServer(ServerAdapter):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2816 def run(self, handler):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2817 from meinheld import server
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2818 server.listen((self.host, self.port))
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2819 server.run(handler)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2820
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2821
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2822 class FapwsServer(ServerAdapter):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2823 """ Extremely fast webserver using libev. See http://www.fapws.org/ """
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2824 def run(self, handler): # pragma: no cover
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2825 import fapws._evwsgi as evwsgi
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2826 from fapws import base, config
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2827 port = self.port
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2828 if float(config.SERVER_IDENT[-2:]) > 0.4:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2829 # fapws3 silently changed its API in 0.5
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2830 port = str(port)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2831 evwsgi.start(self.host, port)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2832 # fapws3 never releases the GIL. Complain upstream. I tried. No luck.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2833 if 'BOTTLE_CHILD' in os.environ and not self.quiet:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2834 _stderr("WARNING: Auto-reloading does not work with Fapws3.\n")
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2835 _stderr(" (Fapws3 breaks python thread support)\n")
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2836 evwsgi.set_base_module(base)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2837 def app(environ, start_response):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2838 environ['wsgi.multiprocess'] = False
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2839 return handler(environ, start_response)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2840 evwsgi.wsgi_cb(('', app))
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2841 evwsgi.run()
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2842
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2843
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2844 class TornadoServer(ServerAdapter):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2845 """ The super hyped asynchronous server by facebook. Untested. """
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2846 def run(self, handler): # pragma: no cover
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2847 import tornado.wsgi, tornado.httpserver, tornado.ioloop
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2848 container = tornado.wsgi.WSGIContainer(handler)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2849 server = tornado.httpserver.HTTPServer(container)
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2850 server.listen(port=self.port,address=self.host)
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2851 tornado.ioloop.IOLoop.instance().start()
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2852
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2853
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2854 class AppEngineServer(ServerAdapter):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2855 """ Adapter for Google App Engine. """
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2856 quiet = True
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2857 def run(self, handler):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2858 from google.appengine.ext.webapp import util
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2859 # A main() function in the handler script enables 'App Caching'.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2860 # Lets makes sure it is there. This _really_ improves performance.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2861 module = sys.modules.get('__main__')
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2862 if module and not hasattr(module, 'main'):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2863 module.main = lambda: util.run_wsgi_app(handler)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2864 util.run_wsgi_app(handler)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2865
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2866
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2867 class TwistedServer(ServerAdapter):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2868 """ Untested. """
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2869 def run(self, handler):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2870 from twisted.web import server, wsgi
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2871 from twisted.python.threadpool import ThreadPool
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2872 from twisted.internet import reactor
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2873 thread_pool = ThreadPool()
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2874 thread_pool.start()
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2875 reactor.addSystemEventTrigger('after', 'shutdown', thread_pool.stop)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2876 factory = server.Site(wsgi.WSGIResource(reactor, thread_pool, handler))
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2877 reactor.listenTCP(self.port, factory, interface=self.host)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2878 reactor.run()
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2879
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2880
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2881 class DieselServer(ServerAdapter):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2882 """ Untested. """
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2883 def run(self, handler):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2884 from diesel.protocols.wsgi import WSGIApplication
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2885 app = WSGIApplication(handler, port=self.port)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2886 app.run()
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2887
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2888
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2889 class GeventServer(ServerAdapter):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2890 """ Untested. Options:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2891
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2892 * `fast` (default: False) uses libevent's http server, but has some
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2893 issues: No streaming, no pipelining, no SSL.
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2894 * See gevent.wsgi.WSGIServer() documentation for more options.
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2895 """
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2896 def run(self, handler):
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2897 from gevent import wsgi, pywsgi, local
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2898 if not isinstance(threading.local(), local.local):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2899 msg = "Bottle requires gevent.monkey.patch_all() (before import)"
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2900 raise RuntimeError(msg)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2901 if not self.options.pop('fast', None): wsgi = pywsgi
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2902 self.options['log'] = None if self.quiet else 'default'
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2903 address = (self.host, self.port)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2904 server = wsgi.WSGIServer(address, handler, **self.options)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2905 if 'BOTTLE_CHILD' in os.environ:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2906 import signal
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2907 signal.signal(signal.SIGINT, lambda s, f: server.stop())
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2908 server.serve_forever()
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2909
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2910
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2911 class GeventSocketIOServer(ServerAdapter):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2912 def run(self,handler):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2913 from socketio import server
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2914 address = (self.host, self.port)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2915 server.SocketIOServer(address, handler, **self.options).serve_forever()
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2916
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2917
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2918 class GunicornServer(ServerAdapter):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2919 """ Untested. See http://gunicorn.org/configure.html for options. """
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2920 def run(self, handler):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2921 from gunicorn.app.base import Application
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2922
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2923 config = {'bind': "%s:%d" % (self.host, int(self.port))}
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2924 config.update(self.options)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2925
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2926 class GunicornApplication(Application):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2927 def init(self, parser, opts, args):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2928 return config
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2929
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2930 def load(self):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2931 return handler
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2932
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2933 GunicornApplication().run()
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2934
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2935
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2936 class EventletServer(ServerAdapter):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2937 """ Untested """
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2938 def run(self, handler):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2939 from eventlet import wsgi, listen
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2940 try:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2941 wsgi.server(listen((self.host, self.port)), handler,
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2942 log_output=(not self.quiet))
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2943 except TypeError:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2944 # Fallback, if we have old version of eventlet
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2945 wsgi.server(listen((self.host, self.port)), handler)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2946
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2947
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2948 class RocketServer(ServerAdapter):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2949 """ Untested. """
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2950 def run(self, handler):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2951 from rocket import Rocket
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2952 server = Rocket((self.host, self.port), 'wsgi', { 'wsgi_app' : handler })
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2953 server.start()
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2954
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2955
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2956 class BjoernServer(ServerAdapter):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2957 """ Fast server written in C: https://github.com/jonashaag/bjoern """
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2958 def run(self, handler):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2959 from bjoern import run
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2960 run(handler, self.host, self.port)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2961
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2962
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2963 class AutoServer(ServerAdapter):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2964 """ Untested. """
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2965 adapters = [WaitressServer, PasteServer, TwistedServer, CherryPyServer, WSGIRefServer]
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2966 def run(self, handler):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2967 for sa in self.adapters:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2968 try:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2969 return sa(self.host, self.port, **self.options).run(handler)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2970 except ImportError:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2971 pass
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2972
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2973 server_names = {
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2974 'cgi': CGIServer,
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2975 'flup': FlupFCGIServer,
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2976 'wsgiref': WSGIRefServer,
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2977 'waitress': WaitressServer,
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2978 'cherrypy': CherryPyServer,
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2979 'paste': PasteServer,
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2980 'fapws3': FapwsServer,
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2981 'tornado': TornadoServer,
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2982 'gae': AppEngineServer,
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2983 'twisted': TwistedServer,
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2984 'diesel': DieselServer,
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2985 'meinheld': MeinheldServer,
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2986 'gunicorn': GunicornServer,
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2987 'eventlet': EventletServer,
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2988 'gevent': GeventServer,
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
2989 'geventSocketIO':GeventSocketIOServer,
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2990 'rocket': RocketServer,
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2991 'bjoern' : BjoernServer,
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2992 'auto': AutoServer,
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2993 }
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2994
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2995
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2996
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2997
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2998
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2999
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3000 ###############################################################################
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3001 # Application Control ##########################################################
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3002 ###############################################################################
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3003
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3004
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3005 def load(target, **namespace):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3006 """ Import a module or fetch an object from a module.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3007
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3008 * ``package.module`` returns `module` as a module object.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3009 * ``pack.mod:name`` returns the module variable `name` from `pack.mod`.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3010 * ``pack.mod:func()`` calls `pack.mod.func()` and returns the result.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3011
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3012 The last form accepts not only function calls, but any type of
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3013 expression. Keyword arguments passed to this function are available as
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3014 local variables. Example: ``import_string('re:compile(x)', x='[a-z]')``
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3015 """
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3016 module, target = target.split(":", 1) if ':' in target else (target, None)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3017 if module not in sys.modules: __import__(module)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3018 if not target: return sys.modules[module]
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3019 if target.isalnum(): return getattr(sys.modules[module], target)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3020 package_name = module.split('.')[0]
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3021 namespace[package_name] = sys.modules[package_name]
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3022 return eval('%s.%s' % (module, target), namespace)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3023
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3024
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3025 def load_app(target):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3026 """ Load a bottle application from a module and make sure that the import
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3027 does not affect the current default application, but returns a separate
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3028 application object. See :func:`load` for the target parameter. """
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3029 global NORUN; NORUN, nr_old = True, NORUN
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3030 try:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3031 tmp = default_app.push() # Create a new "default application"
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3032 rv = load(target) # Import the target module
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3033 return rv if callable(rv) else tmp
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3034 finally:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3035 default_app.remove(tmp) # Remove the temporary added default application
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3036 NORUN = nr_old
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3037
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3038 _debug = debug
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3039 def run(app=None, server='wsgiref', host='127.0.0.1', port=8080,
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3040 interval=1, reloader=False, quiet=False, plugins=None,
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3041 debug=None, **kargs):
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3042 """ Start a server instance. This method blocks until the server terminates.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3043
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3044 :param app: WSGI application or target string supported by
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3045 :func:`load_app`. (default: :func:`default_app`)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3046 :param server: Server adapter to use. See :data:`server_names` keys
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3047 for valid names or pass a :class:`ServerAdapter` subclass.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3048 (default: `wsgiref`)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3049 :param host: Server address to bind to. Pass ``0.0.0.0`` to listens on
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3050 all interfaces including the external one. (default: 127.0.0.1)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3051 :param port: Server port to bind to. Values below 1024 require root
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3052 privileges. (default: 8080)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3053 :param reloader: Start auto-reloading server? (default: False)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3054 :param interval: Auto-reloader interval in seconds (default: 1)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3055 :param quiet: Suppress output to stdout and stderr? (default: False)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3056 :param options: Options passed to the server adapter.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3057 """
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3058 if NORUN: return
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3059 if reloader and not os.environ.get('BOTTLE_CHILD'):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3060 try:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3061 lockfile = None
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3062 fd, lockfile = tempfile.mkstemp(prefix='bottle.', suffix='.lock')
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3063 os.close(fd) # We only need this file to exist. We never write to it
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3064 while os.path.exists(lockfile):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3065 args = [sys.executable] + sys.argv
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3066 environ = os.environ.copy()
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3067 environ['BOTTLE_CHILD'] = 'true'
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3068 environ['BOTTLE_LOCKFILE'] = lockfile
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3069 p = subprocess.Popen(args, env=environ)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3070 while p.poll() is None: # Busy wait...
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3071 os.utime(lockfile, None) # I am alive!
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3072 time.sleep(interval)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3073 if p.poll() != 3:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3074 if os.path.exists(lockfile): os.unlink(lockfile)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3075 sys.exit(p.poll())
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3076 except KeyboardInterrupt:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3077 pass
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3078 finally:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3079 if os.path.exists(lockfile):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3080 os.unlink(lockfile)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3081 return
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3082
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3083 try:
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3084 if debug is not None: _debug(debug)
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3085 app = app or default_app()
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3086 if isinstance(app, basestring):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3087 app = load_app(app)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3088 if not callable(app):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3089 raise ValueError("Application is not callable: %r" % app)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3090
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3091 for plugin in plugins or []:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3092 app.install(plugin)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3093
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3094 if server in server_names:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3095 server = server_names.get(server)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3096 if isinstance(server, basestring):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3097 server = load(server)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3098 if isinstance(server, type):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3099 server = server(host=host, port=port, **kargs)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3100 if not isinstance(server, ServerAdapter):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3101 raise ValueError("Unknown or unsupported server: %r" % server)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3102
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3103 server.quiet = server.quiet or quiet
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3104 if not server.quiet:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3105 _stderr("Bottle v%s server starting up (using %s)...\n" % (__version__, repr(server)))
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3106 _stderr("Listening on http://%s:%d/\n" % (server.host, server.port))
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3107 _stderr("Hit Ctrl-C to quit.\n\n")
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3108
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3109 if reloader:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3110 lockfile = os.environ.get('BOTTLE_LOCKFILE')
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3111 bgcheck = FileCheckerThread(lockfile, interval)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3112 with bgcheck:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3113 server.run(app)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3114 if bgcheck.status == 'reload':
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3115 sys.exit(3)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3116 else:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3117 server.run(app)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3118 except KeyboardInterrupt:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3119 pass
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3120 except (SystemExit, MemoryError):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3121 raise
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3122 except:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3123 if not reloader: raise
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3124 if not getattr(server, 'quiet', quiet):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3125 print_exc()
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3126 time.sleep(interval)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3127 sys.exit(3)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3128
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3129
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3130
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3131 class FileCheckerThread(threading.Thread):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3132 ''' Interrupt main-thread as soon as a changed module file is detected,
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3133 the lockfile gets deleted or gets to old. '''
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3134
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3135 def __init__(self, lockfile, interval):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3136 threading.Thread.__init__(self)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3137 self.lockfile, self.interval = lockfile, interval
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3138 #: Is one of 'reload', 'error' or 'exit'
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3139 self.status = None
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3140
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3141 def run(self):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3142 exists = os.path.exists
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3143 mtime = lambda path: os.stat(path).st_mtime
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3144 files = dict()
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3145
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3146 for module in list(sys.modules.values()):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3147 path = getattr(module, '__file__', '')
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3148 if path[-4:] in ('.pyo', '.pyc'): path = path[:-1]
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3149 if path and exists(path): files[path] = mtime(path)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3150
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3151 while not self.status:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3152 if not exists(self.lockfile)\
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3153 or mtime(self.lockfile) < time.time() - self.interval - 5:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3154 self.status = 'error'
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3155 thread.interrupt_main()
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3156 for path, lmtime in list(files.items()):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3157 if not exists(path) or mtime(path) > lmtime:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3158 self.status = 'reload'
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3159 thread.interrupt_main()
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3160 break
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3161 time.sleep(self.interval)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3162
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3163 def __enter__(self):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3164 self.start()
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3165
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3166 def __exit__(self, exc_type, exc_val, exc_tb):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3167 if not self.status: self.status = 'exit' # silent exit
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3168 self.join()
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3169 return exc_type is not None and issubclass(exc_type, KeyboardInterrupt)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3170
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3171
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3172
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3173
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3174
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3175 ###############################################################################
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3176 # Template Adapters ############################################################
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3177 ###############################################################################
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3178
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3179
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3180 class TemplateError(HTTPError):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3181 def __init__(self, message):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3182 HTTPError.__init__(self, 500, message)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3183
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3184
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3185 class BaseTemplate(object):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3186 """ Base class and minimal API for template adapters """
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3187 extensions = ['tpl','html','thtml','stpl']
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3188 settings = {} #used in prepare()
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3189 defaults = {} #used in render()
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3190
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3191 def __init__(self, source=None, name=None, lookup=[], encoding='utf8', **settings):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3192 """ Create a new template.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3193 If the source parameter (str or buffer) is missing, the name argument
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3194 is used to guess a template filename. Subclasses can assume that
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3195 self.source and/or self.filename are set. Both are strings.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3196 The lookup, encoding and settings parameters are stored as instance
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3197 variables.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3198 The lookup parameter stores a list containing directory paths.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3199 The encoding parameter should be used to decode byte strings or files.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3200 The settings parameter contains a dict for engine-specific settings.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3201 """
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3202 self.name = name
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3203 self.source = source.read() if hasattr(source, 'read') else source
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3204 self.filename = source.filename if hasattr(source, 'filename') else None
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3205 self.lookup = [os.path.abspath(x) for x in lookup]
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3206 self.encoding = encoding
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3207 self.settings = self.settings.copy() # Copy from class variable
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3208 self.settings.update(settings) # Apply
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3209 if not self.source and self.name:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3210 self.filename = self.search(self.name, self.lookup)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3211 if not self.filename:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3212 raise TemplateError('Template %s not found.' % repr(name))
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3213 if not self.source and not self.filename:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3214 raise TemplateError('No template specified.')
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3215 self.prepare(**self.settings)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3216
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3217 @classmethod
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3218 def search(cls, name, lookup=[]):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3219 """ Search name in all directories specified in lookup.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3220 First without, then with common extensions. Return first hit. """
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3221 if not lookup:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3222 depr('The template lookup path list should not be empty.') #0.12
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3223 lookup = ['.']
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3224
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3225 if os.path.isabs(name) and os.path.isfile(name):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3226 depr('Absolute template path names are deprecated.') #0.12
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3227 return os.path.abspath(name)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3228
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3229 for spath in lookup:
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3230 spath = os.path.abspath(spath) + os.sep
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3231 fname = os.path.abspath(os.path.join(spath, name))
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3232 if not fname.startswith(spath): continue
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3233 if os.path.isfile(fname): return fname
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3234 for ext in cls.extensions:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3235 if os.path.isfile('%s.%s' % (fname, ext)):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3236 return '%s.%s' % (fname, ext)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3237
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3238 @classmethod
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3239 def global_config(cls, key, *args):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3240 ''' This reads or sets the global settings stored in class.settings. '''
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3241 if args:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3242 cls.settings = cls.settings.copy() # Make settings local to class
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3243 cls.settings[key] = args[0]
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3244 else:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3245 return cls.settings[key]
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3246
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3247 def prepare(self, **options):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3248 """ Run preparations (parsing, caching, ...).
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3249 It should be possible to call this again to refresh a template or to
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3250 update settings.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3251 """
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3252 raise NotImplementedError
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3253
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3254 def render(self, *args, **kwargs):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3255 """ Render the template with the specified local variables and return
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3256 a single byte or unicode string. If it is a byte string, the encoding
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3257 must match self.encoding. This method must be thread-safe!
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3258 Local variables may be provided in dictionaries (args)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3259 or directly, as keywords (kwargs).
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3260 """
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3261 raise NotImplementedError
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3262
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3263
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3264 class MakoTemplate(BaseTemplate):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3265 def prepare(self, **options):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3266 from mako.template import Template
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3267 from mako.lookup import TemplateLookup
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3268 options.update({'input_encoding':self.encoding})
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3269 options.setdefault('format_exceptions', bool(DEBUG))
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3270 lookup = TemplateLookup(directories=self.lookup, **options)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3271 if self.source:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3272 self.tpl = Template(self.source, lookup=lookup, **options)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3273 else:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3274 self.tpl = Template(uri=self.name, filename=self.filename, lookup=lookup, **options)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3275
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3276 def render(self, *args, **kwargs):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3277 for dictarg in args: kwargs.update(dictarg)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3278 _defaults = self.defaults.copy()
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3279 _defaults.update(kwargs)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3280 return self.tpl.render(**_defaults)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3281
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3282
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3283 class CheetahTemplate(BaseTemplate):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3284 def prepare(self, **options):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3285 from Cheetah.Template import Template
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3286 self.context = threading.local()
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3287 self.context.vars = {}
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3288 options['searchList'] = [self.context.vars]
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3289 if self.source:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3290 self.tpl = Template(source=self.source, **options)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3291 else:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3292 self.tpl = Template(file=self.filename, **options)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3293
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3294 def render(self, *args, **kwargs):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3295 for dictarg in args: kwargs.update(dictarg)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3296 self.context.vars.update(self.defaults)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3297 self.context.vars.update(kwargs)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3298 out = str(self.tpl)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3299 self.context.vars.clear()
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3300 return out
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3301
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3302
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3303 class Jinja2Template(BaseTemplate):
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3304 def prepare(self, filters=None, tests=None, globals={}, **kwargs):
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3305 from jinja2 import Environment, FunctionLoader
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3306 if 'prefix' in kwargs: # TODO: to be removed after a while
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3307 raise RuntimeError('The keyword argument `prefix` has been removed. '
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3308 'Use the full jinja2 environment name line_statement_prefix instead.')
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3309 self.env = Environment(loader=FunctionLoader(self.loader), **kwargs)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3310 if filters: self.env.filters.update(filters)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3311 if tests: self.env.tests.update(tests)
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3312 if globals: self.env.globals.update(globals)
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3313 if self.source:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3314 self.tpl = self.env.from_string(self.source)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3315 else:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3316 self.tpl = self.env.get_template(self.filename)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3317
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3318 def render(self, *args, **kwargs):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3319 for dictarg in args: kwargs.update(dictarg)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3320 _defaults = self.defaults.copy()
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3321 _defaults.update(kwargs)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3322 return self.tpl.render(**_defaults)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3323
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3324 def loader(self, name):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3325 fname = self.search(name, self.lookup)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3326 if not fname: return
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3327 with open(fname, "rb") as f:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3328 return f.read().decode(self.encoding)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3329
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3330
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3331 class SimpleTemplate(BaseTemplate):
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3332
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3333 def prepare(self, escape_func=html_escape, noescape=False, syntax=None, **ka):
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3334 self.cache = {}
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3335 enc = self.encoding
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3336 self._str = lambda x: touni(x, enc)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3337 self._escape = lambda x: escape_func(touni(x, enc))
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3338 self.syntax = syntax
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3339 if noescape:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3340 self._str, self._escape = self._escape, self._str
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3341
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3342 @cached_property
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3343 def co(self):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3344 return compile(self.code, self.filename or '<string>', 'exec')
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3345
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3346 @cached_property
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3347 def code(self):
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3348 source = self.source
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3349 if not source:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3350 with open(self.filename, 'rb') as f:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3351 source = f.read()
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3352 try:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3353 source, encoding = touni(source), 'utf8'
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3354 except UnicodeError:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3355 depr('Template encodings other than utf8 are no longer supported.') #0.11
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3356 source, encoding = touni(source, 'latin1'), 'latin1'
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3357 parser = StplParser(source, encoding=encoding, syntax=self.syntax)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3358 code = parser.translate()
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3359 self.encoding = parser.encoding
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3360 return code
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3361
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3362 def _rebase(self, _env, _name=None, **kwargs):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3363 if _name is None:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3364 depr('Rebase function called without arguments.'
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3365 ' You were probably looking for {{base}}?', True) #0.12
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3366 _env['_rebase'] = (_name, kwargs)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3367
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3368 def _include(self, _env, _name=None, **kwargs):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3369 if _name is None:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3370 depr('Rebase function called without arguments.'
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3371 ' You were probably looking for {{base}}?', True) #0.12
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3372 env = _env.copy()
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3373 env.update(kwargs)
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3374 if _name not in self.cache:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3375 self.cache[_name] = self.__class__(name=_name, lookup=self.lookup)
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3376 return self.cache[_name].execute(env['_stdout'], env)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3377
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3378 def execute(self, _stdout, kwargs):
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3379 env = self.defaults.copy()
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3380 env.update(kwargs)
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3381 env.update({'_stdout': _stdout, '_printlist': _stdout.extend,
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3382 'include': functools.partial(self._include, env),
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3383 'rebase': functools.partial(self._rebase, env), '_rebase': None,
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3384 '_str': self._str, '_escape': self._escape, 'get': env.get,
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3385 'setdefault': env.setdefault, 'defined': env.__contains__ })
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3386 eval(self.co, env)
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3387 if env.get('_rebase'):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3388 subtpl, rargs = env.pop('_rebase')
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3389 rargs['base'] = ''.join(_stdout) #copy stdout
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3390 del _stdout[:] # clear stdout
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3391 return self._include(env, subtpl, **rargs)
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3392 return env
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3393
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3394 def render(self, *args, **kwargs):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3395 """ Render the template using keyword arguments as local variables. """
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3396 env = {}; stdout = []
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3397 for dictarg in args: env.update(dictarg)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3398 env.update(kwargs)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3399 self.execute(stdout, env)
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3400 return ''.join(stdout)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3401
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3402
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3403 class StplSyntaxError(TemplateError): pass
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3404
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3405
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3406 class StplParser(object):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3407 ''' Parser for stpl templates. '''
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3408 _re_cache = {} #: Cache for compiled re patterns
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3409 # This huge pile of voodoo magic splits python code into 8 different tokens.
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3410 # 1: All kinds of python strings (trust me, it works)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3411 _re_tok = '((?m)[urbURB]?(?:\'\'(?!\')|""(?!")|\'{6}|"{6}' \
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3412 '|\'(?:[^\\\\\']|\\\\.)+?\'|"(?:[^\\\\"]|\\\\.)+?"' \
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3413 '|\'{3}(?:[^\\\\]|\\\\.|\\n)+?\'{3}' \
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3414 '|"{3}(?:[^\\\\]|\\\\.|\\n)+?"{3}))'
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3415 _re_inl = _re_tok.replace('|\\n','') # We re-use this string pattern later
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3416 # 2: Comments (until end of line, but not the newline itself)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3417 _re_tok += '|(#.*)'
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3418 # 3,4: Keywords that start or continue a python block (only start of line)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3419 _re_tok += '|^([ \\t]*(?:if|for|while|with|try|def|class)\\b)' \
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3420 '|^([ \\t]*(?:elif|else|except|finally)\\b)'
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3421 # 5: Our special 'end' keyword (but only if it stands alone)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3422 _re_tok += '|((?:^|;)[ \\t]*end[ \\t]*(?=(?:%(block_close)s[ \\t]*)?\\r?$|;|#))'
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3423 # 6: A customizable end-of-code-block template token (only end of line)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3424 _re_tok += '|(%(block_close)s[ \\t]*(?=$))'
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3425 # 7: And finally, a single newline. The 8th token is 'everything else'
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3426 _re_tok += '|(\\r?\\n)'
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3427 # Match the start tokens of code areas in a template
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3428 _re_split = '(?m)^[ \t]*(\\\\?)((%(line_start)s)|(%(block_start)s))(%%?)'
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3429 # Match inline statements (may contain python strings)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3430 _re_inl = '%%(inline_start)s((?:%s|[^\'"\n]*?)+)%%(inline_end)s' % _re_inl
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3431
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3432 default_syntax = '<% %> % {{ }}'
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3433
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3434 def __init__(self, source, syntax=None, encoding='utf8'):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3435 self.source, self.encoding = touni(source, encoding), encoding
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3436 self.set_syntax(syntax or self.default_syntax)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3437 self.code_buffer, self.text_buffer = [], []
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3438 self.lineno, self.offset = 1, 0
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3439 self.indent, self.indent_mod = 0, 0
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3440
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3441 def get_syntax(self):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3442 ''' Tokens as a space separated string (default: <% %> % {{ }}) '''
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3443 return self._syntax
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3444
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3445 def set_syntax(self, syntax):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3446 self._syntax = syntax
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3447 self._tokens = syntax.split()
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3448 if not syntax in self._re_cache:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3449 names = 'block_start block_close line_start inline_start inline_end'
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3450 etokens = map(re.escape, self._tokens)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3451 pattern_vars = dict(zip(names.split(), etokens))
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3452 patterns = (self._re_split, self._re_tok, self._re_inl)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3453 patterns = [re.compile(p%pattern_vars) for p in patterns]
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3454 self._re_cache[syntax] = patterns
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3455 self.re_split, self.re_tok, self.re_inl = self._re_cache[syntax]
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3456
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3457 syntax = property(get_syntax, set_syntax)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3458
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3459 def translate(self):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3460 if self.offset: raise RuntimeError('Parser is a one time instance.')
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3461 while True:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3462 m = self.re_split.search(self.source[self.offset:])
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3463 if m:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3464 text = self.source[self.offset:self.offset+m.start()]
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3465 self.text_buffer.append(text)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3466 self.offset += m.end()
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3467 if m.group(1): # New escape syntax
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3468 line, sep, _ = self.source[self.offset:].partition('\n')
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3469 self.text_buffer.append(m.group(2)+m.group(5)+line+sep)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3470 self.offset += len(line+sep)+1
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3471 continue
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3472 elif m.group(5): # Old escape syntax
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3473 depr('Escape code lines with a backslash.') #0.12
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3474 line, sep, _ = self.source[self.offset:].partition('\n')
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3475 self.text_buffer.append(m.group(2)+line+sep)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3476 self.offset += len(line+sep)+1
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3477 continue
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3478 self.flush_text()
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3479 self.read_code(multiline=bool(m.group(4)))
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3480 else: break
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3481 self.text_buffer.append(self.source[self.offset:])
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3482 self.flush_text()
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3483 return ''.join(self.code_buffer)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3484
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3485 def read_code(self, multiline):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3486 code_line, comment = '', ''
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3487 while True:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3488 m = self.re_tok.search(self.source[self.offset:])
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3489 if not m:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3490 code_line += self.source[self.offset:]
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3491 self.offset = len(self.source)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3492 self.write_code(code_line.strip(), comment)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3493 return
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3494 code_line += self.source[self.offset:self.offset+m.start()]
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3495 self.offset += m.end()
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3496 _str, _com, _blk1, _blk2, _end, _cend, _nl = m.groups()
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3497 if code_line and (_blk1 or _blk2): # a if b else c
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3498 code_line += _blk1 or _blk2
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3499 continue
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3500 if _str: # Python string
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3501 code_line += _str
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3502 elif _com: # Python comment (up to EOL)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3503 comment = _com
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3504 if multiline and _com.strip().endswith(self._tokens[1]):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3505 multiline = False # Allow end-of-block in comments
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3506 elif _blk1: # Start-block keyword (if/for/while/def/try/...)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3507 code_line, self.indent_mod = _blk1, -1
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3508 self.indent += 1
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3509 elif _blk2: # Continue-block keyword (else/elif/except/...)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3510 code_line, self.indent_mod = _blk2, -1
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3511 elif _end: # The non-standard 'end'-keyword (ends a block)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3512 self.indent -= 1
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3513 elif _cend: # The end-code-block template token (usually '%>')
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3514 if multiline: multiline = False
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3515 else: code_line += _cend
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3516 else: # \n
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3517 self.write_code(code_line.strip(), comment)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3518 self.lineno += 1
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3519 code_line, comment, self.indent_mod = '', '', 0
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3520 if not multiline:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3521 break
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3522
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3523 def flush_text(self):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3524 text = ''.join(self.text_buffer)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3525 del self.text_buffer[:]
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3526 if not text: return
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3527 parts, pos, nl = [], 0, '\\\n'+' '*self.indent
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3528 for m in self.re_inl.finditer(text):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3529 prefix, pos = text[pos:m.start()], m.end()
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3530 if prefix:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3531 parts.append(nl.join(map(repr, prefix.splitlines(True))))
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3532 if prefix.endswith('\n'): parts[-1] += nl
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3533 parts.append(self.process_inline(m.group(1).strip()))
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3534 if pos < len(text):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3535 prefix = text[pos:]
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3536 lines = prefix.splitlines(True)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3537 if lines[-1].endswith('\\\\\n'): lines[-1] = lines[-1][:-3]
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3538 elif lines[-1].endswith('\\\\\r\n'): lines[-1] = lines[-1][:-4]
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3539 parts.append(nl.join(map(repr, lines)))
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3540 code = '_printlist((%s,))' % ', '.join(parts)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3541 self.lineno += code.count('\n')+1
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3542 self.write_code(code)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3543
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3544 def process_inline(self, chunk):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3545 if chunk[0] == '!': return '_str(%s)' % chunk[1:]
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3546 return '_escape(%s)' % chunk
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3547
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3548 def write_code(self, line, comment=''):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3549 line, comment = self.fix_backward_compatibility(line, comment)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3550 code = ' ' * (self.indent+self.indent_mod)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3551 code += line.lstrip() + comment + '\n'
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3552 self.code_buffer.append(code)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3553
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3554 def fix_backward_compatibility(self, line, comment):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3555 parts = line.strip().split(None, 2)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3556 if parts and parts[0] in ('include', 'rebase'):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3557 depr('The include and rebase keywords are functions now.') #0.12
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3558 if len(parts) == 1: return "_printlist([base])", comment
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3559 elif len(parts) == 2: return "_=%s(%r)" % tuple(parts), comment
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3560 else: return "_=%s(%r, %s)" % tuple(parts), comment
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3561 if self.lineno <= 2 and not line.strip() and 'coding' in comment:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3562 m = re.match(r"#.*coding[:=]\s*([-\w.]+)", comment)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3563 if m:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3564 depr('PEP263 encoding strings in templates are deprecated.') #0.12
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3565 enc = m.group(1)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3566 self.source = self.source.encode(self.encoding).decode(enc)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3567 self.encoding = enc
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3568 return line, comment.replace('coding','coding*')
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3569 return line, comment
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3570
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3571
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3572 def template(*args, **kwargs):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3573 '''
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3574 Get a rendered template as a string iterator.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3575 You can use a name, a filename or a template string as first parameter.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3576 Template rendering arguments can be passed as dictionaries
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3577 or directly (as keyword arguments).
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3578 '''
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3579 tpl = args[0] if args else None
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3580 adapter = kwargs.pop('template_adapter', SimpleTemplate)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3581 lookup = kwargs.pop('template_lookup', TEMPLATE_PATH)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3582 tplid = (id(lookup), tpl)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3583 if tplid not in TEMPLATES or DEBUG:
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3584 settings = kwargs.pop('template_settings', {})
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3585 if isinstance(tpl, adapter):
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3586 TEMPLATES[tplid] = tpl
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3587 if settings: TEMPLATES[tplid].prepare(**settings)
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3588 elif "\n" in tpl or "{" in tpl or "%" in tpl or '$' in tpl:
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3589 TEMPLATES[tplid] = adapter(source=tpl, lookup=lookup, **settings)
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3590 else:
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3591 TEMPLATES[tplid] = adapter(name=tpl, lookup=lookup, **settings)
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3592 if not TEMPLATES[tplid]:
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3593 abort(500, 'Template (%s) not found' % tpl)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3594 for dictarg in args[1:]: kwargs.update(dictarg)
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3595 return TEMPLATES[tplid].render(kwargs)
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3596
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3597 mako_template = functools.partial(template, template_adapter=MakoTemplate)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3598 cheetah_template = functools.partial(template, template_adapter=CheetahTemplate)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3599 jinja2_template = functools.partial(template, template_adapter=Jinja2Template)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3600
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3601
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3602 def view(tpl_name, **defaults):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3603 ''' Decorator: renders a template for a handler.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3604 The handler can control its behavior like that:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3605
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3606 - return a dict of template vars to fill out the template
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3607 - return something other than a dict and the view decorator will not
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3608 process the template, but return the handler result as is.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3609 This includes returning a HTTPResponse(dict) to get,
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3610 for instance, JSON with autojson or other castfilters.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3611 '''
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3612 def decorator(func):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3613 @functools.wraps(func)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3614 def wrapper(*args, **kwargs):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3615 result = func(*args, **kwargs)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3616 if isinstance(result, (dict, DictMixin)):
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3617 tplvars = defaults.copy()
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3618 tplvars.update(result)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3619 return template(tpl_name, **tplvars)
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3620 elif result is None:
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3621 return template(tpl_name, defaults)
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3622 return result
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3623 return wrapper
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3624 return decorator
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3625
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3626 mako_view = functools.partial(view, template_adapter=MakoTemplate)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3627 cheetah_view = functools.partial(view, template_adapter=CheetahTemplate)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3628 jinja2_view = functools.partial(view, template_adapter=Jinja2Template)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3629
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3630
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3631
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3632
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3633
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3634
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3635 ###############################################################################
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3636 # Constants and Globals ########################################################
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3637 ###############################################################################
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3638
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3639
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3640 TEMPLATE_PATH = ['./', './views/']
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3641 TEMPLATES = {}
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3642 DEBUG = False
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3643 NORUN = False # If set, run() does nothing. Used by load_app()
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3644
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3645 #: A dict to map HTTP status codes (e.g. 404) to phrases (e.g. 'Not Found')
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3646 HTTP_CODES = httplib.responses
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3647 HTTP_CODES[418] = "I'm a teapot" # RFC 2324
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3648 HTTP_CODES[422] = "Unprocessable Entity" # RFC 4918
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3649 HTTP_CODES[428] = "Precondition Required"
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3650 HTTP_CODES[429] = "Too Many Requests"
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3651 HTTP_CODES[431] = "Request Header Fields Too Large"
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3652 HTTP_CODES[511] = "Network Authentication Required"
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3653 _HTTP_STATUS_LINES = dict((k, '%d %s'%(k,v)) for (k,v) in HTTP_CODES.items())
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3654
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3655 #: The default template used for error pages. Override with @error()
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3656 ERROR_PAGE_TEMPLATE = """
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3657 %%try:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3658 %%from %s import DEBUG, HTTP_CODES, request, touni
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3659 <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3660 <html>
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3661 <head>
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3662 <title>Error: {{e.status}}</title>
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3663 <style type="text/css">
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3664 html {background-color: #eee; font-family: sans;}
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3665 body {background-color: #fff; border: 1px solid #ddd;
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3666 padding: 15px; margin: 15px;}
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3667 pre {background-color: #eee; border: 1px solid #ddd; padding: 5px;}
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3668 </style>
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3669 </head>
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3670 <body>
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3671 <h1>Error: {{e.status}}</h1>
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3672 <p>Sorry, the requested URL <tt>{{repr(request.url)}}</tt>
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3673 caused an error:</p>
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3674 <pre>{{e.body}}</pre>
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3675 %%if DEBUG and e.exception:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3676 <h2>Exception:</h2>
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3677 <pre>{{repr(e.exception)}}</pre>
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3678 %%end
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3679 %%if DEBUG and e.traceback:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3680 <h2>Traceback:</h2>
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3681 <pre>{{e.traceback}}</pre>
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3682 %%end
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3683 </body>
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3684 </html>
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3685 %%except ImportError:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3686 <b>ImportError:</b> Could not generate the error page. Please add bottle to
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3687 the import path.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3688 %%end
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3689 """ % __name__
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3690
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3691 #: A thread-safe instance of :class:`LocalRequest`. If accessed from within a
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3692 #: request callback, this instance always refers to the *current* request
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3693 #: (even on a multithreaded server).
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3694 request = LocalRequest()
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3695
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3696 #: A thread-safe instance of :class:`LocalResponse`. It is used to change the
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3697 #: HTTP response for the *current* request.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3698 response = LocalResponse()
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3699
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3700 #: A thread-safe namespace. Not used by Bottle.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3701 local = threading.local()
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3702
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3703 # Initialize app stack (create first empty Bottle app)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3704 # BC: 0.6.4 and needed for run()
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3705 app = default_app = AppStack()
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3706 app.push()
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3707
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3708 #: A virtual package that redirects import statements.
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3709 #: Example: ``import bottle.ext.sqlite`` actually imports `bottle_sqlite`.
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3710 ext = _ImportRedirect('bottle.ext' if __name__ == '__main__' else __name__+".ext", 'bottle_%s').module
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3711
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3712 if __name__ == '__main__':
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3713 opt, args, parser = _cmd_options, _cmd_args, _cmd_parser
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3714 if opt.version:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3715 _stdout('Bottle %s\n'%__version__)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3716 sys.exit(0)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3717 if not args:
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3718 parser.print_help()
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3719 _stderr('\nError: No application specified.\n')
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3720 sys.exit(1)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3721
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3722 sys.path.insert(0, '.')
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3723 sys.modules.setdefault('bottle', sys.modules['__main__'])
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3724
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3725 host, port = (opt.bind or 'localhost'), 8080
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3726 if ':' in host and host.rfind(']') < host.rfind(':'):
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3727 host, port = host.rsplit(':', 1)
248
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3728 host = host.strip('[]')
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3729
317a1738f15c Update to bottle 0.12.8
Matt Johnston <matt@ucc.asn.au>
parents: 27
diff changeset
3730 run(args[0], host=host, port=int(port), server=opt.server,
27
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3731 reloader=opt.reload, plugins=opt.plugin, debug=opt.debug)
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3732
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3733
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3734
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3735
dbbd503119ba Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3736 # THE END