annotate web/bottle.py @ 333:298e502fdcd4

Add some web server handling
author Matt Johnston <matt@ucc.asn.au>
date Tue, 12 Jun 2012 00:09:09 +0800
parents
children 317a1738f15c
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
333
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1 #!/usr/bin/env python
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2 # -*- coding: utf-8 -*-
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3 """
298e502fdcd4 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
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
5 offers request dispatching (Routes) with url parameter support, templates,
298e502fdcd4 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
298e502fdcd4 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
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
8 Python Standard Library.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
9
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
10 Homepage and documentation: http://bottlepy.org/
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
11
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
12 Copyright (c) 2011, Marcel Hellkamp.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
13 License: MIT (see LICENSE for details)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
14 """
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
15
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
16 from __future__ import with_statement
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
17
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
18 __author__ = 'Marcel Hellkamp'
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
19 __version__ = '0.11.dev'
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
20 __license__ = 'MIT'
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
21
298e502fdcd4 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
298e502fdcd4 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
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
24 if __name__ == '__main__':
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
25 from optparse import OptionParser
298e502fdcd4 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")
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
27 _opt = _cmd_parser.add_option
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
28 _opt("--version", action="store_true", help="show version number.")
298e502fdcd4 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.")
298e502fdcd4 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.")
298e502fdcd4 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.")
298e502fdcd4 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.")
298e502fdcd4 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.")
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
34 _cmd_options, _cmd_args = _cmd_parser.parse_args()
298e502fdcd4 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'):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
36 import gevent.monkey; gevent.monkey.patch_all()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
37
298e502fdcd4 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,\
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
39 os, re, subprocess, sys, tempfile, threading, time, urllib, warnings
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
40
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
41 from datetime import date as datedate, datetime, timedelta
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
42 from tempfile import TemporaryFile
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
43 from traceback import format_exc, print_exc
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
44
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
45 try: from json import dumps as json_dumps, loads as json_lds
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
46 except ImportError: # pragma: no cover
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
47 try: from simplejson import dumps as json_dumps, loads as json_lds
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
48 except ImportError:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
49 try: from django.utils.simplejson import dumps as json_dumps, loads as json_lds
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
50 except ImportError:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
51 def json_dumps(data):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
52 raise ImportError("JSON support requires Python 2.6 or simplejson.")
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
53 json_lds = json_dumps
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
54
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
55
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
56
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
57 # We now try to fix 2.5/2.6/3.1/3.2 incompatibilities.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
58 # It ain't pretty but it works... Sorry for the mess.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
59
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
60 py = sys.version_info
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
61 py3k = py >= (3,0,0)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
62 py25 = py < (2,6,0)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
63
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
64 # Workaround for the missing "as" keyword in py3k.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
65 def _e(): return sys.exc_info()[1]
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
66
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
67 # Workaround for the "print is a keyword/function" Python 2/3 dilemma
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
68 # and a fallback for mod_wsgi (resticts stdout/err attribute access)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
69 try:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
70 _stdout, _stderr = sys.stdout.write, sys.stderr.write
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
71 except IOError:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
72 _stdout = lambda x: sys.stdout.write(x)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
73 _stderr = lambda x: sys.stderr.write(x)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
74
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
75 # Lots of stdlib and builtin differences.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
76 if py3k:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
77 import http.client as httplib
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
78 import _thread as thread
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
79 from urllib.parse import urljoin, parse_qsl, SplitResult as UrlSplitResult
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
80 from urllib.parse import urlencode, quote as urlquote, unquote as urlunquote
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
81 from http.cookies import SimpleCookie
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
82 from collections import MutableMapping as DictMixin
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
83 import pickle
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
84 from io import BytesIO
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
85 basestring = str
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
86 unicode = str
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
87 json_loads = lambda s: json_lds(touni(s))
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
88 callable = lambda x: hasattr(x, '__call__')
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
89 imap = map
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
90 else: # 2.x
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
91 import httplib
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
92 import thread
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
93 from urlparse import urljoin, SplitResult as UrlSplitResult
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
94 from urllib import urlencode, quote as urlquote, unquote as urlunquote
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
95 from Cookie import SimpleCookie
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
96 from itertools import imap
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
97 import cPickle as pickle
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
98 from StringIO import StringIO as BytesIO
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
99 if py25:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
100 msg = "Python 2.5 support may be dropped in future versions of Bottle."
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
101 warnings.warn(msg, DeprecationWarning)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
102 from cgi import parse_qsl
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
103 from UserDict import DictMixin
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
104 def next(it): return it.next()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
105 bytes = str
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
106 else: # 2.6, 2.7
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
107 from urlparse import parse_qsl
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
108 from collections import MutableMapping as DictMixin
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
109 json_loads = json_lds
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
110
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
111 # Some helpers for string/byte handling
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
112 def tob(s, enc='utf8'):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
113 return s.encode(enc) if isinstance(s, unicode) else bytes(s)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
114 def touni(s, enc='utf8', err='strict'):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
115 return s.decode(enc, err) if isinstance(s, bytes) else unicode(s)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
116 tonat = touni if py3k else tob
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
117
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
118 # 3.2 fixes cgi.FieldStorage to accept bytes (which makes a lot of sense).
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
119 # 3.1 needs a workaround.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
120 NCTextIOWrapper = None
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
121 if (3,0,0) < py < (3,2,0):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
122 from io import TextIOWrapper
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
123 class NCTextIOWrapper(TextIOWrapper):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
124 def close(self): pass # Keep wrapped buffer open.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
125
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
126 # A bug in functools causes it to break if the wrapper is an instance method
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
127 def update_wrapper(wrapper, wrapped, *a, **ka):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
128 try: functools.update_wrapper(wrapper, wrapped, *a, **ka)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
129 except AttributeError: pass
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
130
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
131
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
132
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
133 # These helpers are used at module level and need to be defined first.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
134 # And yes, I know PEP-8, but sometimes a lower-case classname makes more sense.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
135
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
136 def depr(message):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
137 warnings.warn(message, DeprecationWarning, stacklevel=3)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
138
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
139 def makelist(data): # This is just to handy
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
140 if isinstance(data, (tuple, list, set, dict)): return list(data)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
141 elif data: return [data]
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
142 else: return []
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
143
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
144
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
145 class DictProperty(object):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
146 ''' Property that maps to a key in a local dict-like attribute. '''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
147 def __init__(self, attr, key=None, read_only=False):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
148 self.attr, self.key, self.read_only = attr, key, read_only
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
149
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
150 def __call__(self, func):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
151 functools.update_wrapper(self, func, updated=[])
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
152 self.getter, self.key = func, self.key or func.__name__
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
153 return self
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
154
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
155 def __get__(self, obj, cls):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
156 if obj is None: return self
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
157 key, storage = self.key, getattr(obj, self.attr)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
158 if key not in storage: storage[key] = self.getter(obj)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
159 return storage[key]
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
160
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
161 def __set__(self, obj, value):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
162 if self.read_only: raise AttributeError("Read-Only property.")
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
163 getattr(obj, self.attr)[self.key] = value
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
164
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
165 def __delete__(self, obj):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
166 if self.read_only: raise AttributeError("Read-Only property.")
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
167 del getattr(obj, self.attr)[self.key]
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
168
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
169
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
170 class cached_property(object):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
171 ''' A property that is only computed once per instance and then replaces
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
172 itself with an ordinary attribute. Deleting the attribute resets the
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
173 property. '''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
174
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
175 def __init__(self, func):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
176 self.func = func
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
177
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
178 def __get__(self, obj, cls):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
179 if obj is None: return self
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
180 value = obj.__dict__[self.func.__name__] = self.func(obj)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
181 return value
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
182
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
183
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
184 class lazy_attribute(object):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
185 ''' A property that caches itself to the class object. '''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
186 def __init__(self, func):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
187 functools.update_wrapper(self, func, updated=[])
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
188 self.getter = func
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
189
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
190 def __get__(self, obj, cls):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
191 value = self.getter(cls)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
192 setattr(cls, self.__name__, value)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
193 return value
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
194
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
195
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
196
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
197
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
198
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
199
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
200 ###############################################################################
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
201 # Exceptions and Events ########################################################
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
202 ###############################################################################
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
203
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
204
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
205 class BottleException(Exception):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
206 """ A base class for exceptions used by bottle. """
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
207 pass
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
208
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
209
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
210 #TODO: This should subclass BaseRequest
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
211 class HTTPResponse(BottleException):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
212 """ Used to break execution and immediately finish the response """
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
213 def __init__(self, output='', status=200, header=None):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
214 super(BottleException, self).__init__("HTTP Response %d" % status)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
215 self.status = int(status)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
216 self.output = output
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
217 self.headers = HeaderDict(header) if header else None
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
218
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
219 def apply(self, response):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
220 if self.headers:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
221 for key, value in self.headers.allitems():
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
222 response.headers[key] = value
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
223 response.status = self.status
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
224
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
225
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
226 class HTTPError(HTTPResponse):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
227 """ Used to generate an error page """
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
228 def __init__(self, code=500, output='Unknown Error', exception=None,
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
229 traceback=None, header=None):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
230 super(HTTPError, self).__init__(output, code, header)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
231 self.exception = exception
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
232 self.traceback = traceback
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
233
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
234 def __repr__(self):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
235 return tonat(template(ERROR_PAGE_TEMPLATE, e=self))
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
236
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
237
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
238
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
239
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
240
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
241
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
242 ###############################################################################
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
243 # Routing ######################################################################
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
244 ###############################################################################
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
245
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
246
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
247 class RouteError(BottleException):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
248 """ This is a base class for all routing related exceptions """
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
249
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
250
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
251 class RouteReset(BottleException):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
252 """ If raised by a plugin or request handler, the route is reset and all
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
253 plugins are re-applied. """
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
254
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
255 class RouterUnknownModeError(RouteError): pass
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
256
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
257
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
258 class RouteSyntaxError(RouteError):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
259 """ The route parser found something not supported by this router """
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
260
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
261
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
262 class RouteBuildError(RouteError):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
263 """ The route could not been built """
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
264
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
265
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
266 class Router(object):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
267 ''' A Router is an ordered collection of route->target pairs. It is used to
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
268 efficiently match WSGI requests against a number of routes and return
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
269 the first target that satisfies the request. The target may be anything,
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
270 usually a string, ID or callable object. A route consists of a path-rule
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
271 and a HTTP method.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
272
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
273 The path-rule is either a static path (e.g. `/contact`) or a dynamic
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
274 path that contains wildcards (e.g. `/wiki/<page>`). The wildcard syntax
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
275 and details on the matching order are described in docs:`routing`.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
276 '''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
277
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
278 default_pattern = '[^/]+'
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
279 default_filter = 're'
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
280 #: Sorry for the mess. It works. Trust me.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
281 rule_syntax = re.compile('(\\\\*)'\
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
282 '(?:(?::([a-zA-Z_][a-zA-Z_0-9]*)?()(?:#(.*?)#)?)'\
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
283 '|(?:<([a-zA-Z_][a-zA-Z_0-9]*)?(?::([a-zA-Z_]*)'\
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
284 '(?::((?:\\\\.|[^\\\\>]+)+)?)?)?>))')
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
285
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
286 def __init__(self, strict=False):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
287 self.rules = {} # A {rule: Rule} mapping
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
288 self.builder = {} # A rule/name->build_info mapping
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
289 self.static = {} # Cache for static routes: {path: {method: target}}
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
290 self.dynamic = [] # Cache for dynamic routes. See _compile()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
291 #: If true, static routes are no longer checked first.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
292 self.strict_order = strict
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
293 self.filters = {'re': self.re_filter, 'int': self.int_filter,
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
294 'float': self.float_filter, 'path': self.path_filter}
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
295
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
296 def re_filter(self, conf):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
297 return conf or self.default_pattern, None, None
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
298
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
299 def int_filter(self, conf):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
300 return r'-?\d+', int, lambda x: str(int(x))
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
301
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
302 def float_filter(self, conf):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
303 return r'-?[\d.]+', float, lambda x: str(float(x))
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
304
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
305 def path_filter(self, conf):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
306 return r'.+?', None, None
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
307
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
308 def add_filter(self, name, func):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
309 ''' Add a filter. The provided function is called with the configuration
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
310 string as parameter and must return a (regexp, to_python, to_url) tuple.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
311 The first element is a string, the last two are callables or None. '''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
312 self.filters[name] = func
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
313
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
314 def parse_rule(self, rule):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
315 ''' Parses a rule into a (name, filter, conf) token stream. If mode is
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
316 None, name contains a static rule part. '''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
317 offset, prefix = 0, ''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
318 for match in self.rule_syntax.finditer(rule):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
319 prefix += rule[offset:match.start()]
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
320 g = match.groups()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
321 if len(g[0])%2: # Escaped wildcard
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
322 prefix += match.group(0)[len(g[0]):]
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
323 offset = match.end()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
324 continue
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
325 if prefix: yield prefix, None, None
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
326 name, filtr, conf = g[1:4] if not g[2] is None else g[4:7]
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
327 if not filtr: filtr = self.default_filter
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
328 yield name, filtr, conf or None
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
329 offset, prefix = match.end(), ''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
330 if offset <= len(rule) or prefix:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
331 yield prefix+rule[offset:], None, None
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
332
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
333 def add(self, rule, method, target, name=None):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
334 ''' Add a new route or replace the target for an existing route. '''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
335 if rule in self.rules:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
336 self.rules[rule][method] = target
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
337 if name: self.builder[name] = self.builder[rule]
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
338 return
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
339
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
340 target = self.rules[rule] = {method: target}
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
341
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
342 # Build pattern and other structures for dynamic routes
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
343 anons = 0 # Number of anonymous wildcards
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
344 pattern = '' # Regular expression pattern
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
345 filters = [] # Lists of wildcard input filters
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
346 builder = [] # Data structure for the URL builder
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
347 is_static = True
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
348 for key, mode, conf in self.parse_rule(rule):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
349 if mode:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
350 is_static = False
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
351 mask, in_filter, out_filter = self.filters[mode](conf)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
352 if key:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
353 pattern += '(?P<%s>%s)' % (key, mask)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
354 else:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
355 pattern += '(?:%s)' % mask
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
356 key = 'anon%d' % anons; anons += 1
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
357 if in_filter: filters.append((key, in_filter))
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
358 builder.append((key, out_filter or str))
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
359 elif key:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
360 pattern += re.escape(key)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
361 builder.append((None, key))
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
362 self.builder[rule] = builder
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
363 if name: self.builder[name] = builder
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
364
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
365 if is_static and not self.strict_order:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
366 self.static[self.build(rule)] = target
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
367 return
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
368
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
369 def fpat_sub(m):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
370 return m.group(0) if len(m.group(1)) % 2 else m.group(1) + '(?:'
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
371 flat_pattern = re.sub(r'(\\*)(\(\?P<[^>]*>|\((?!\?))', fpat_sub, pattern)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
372
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
373 try:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
374 re_match = re.compile('^(%s)$' % pattern).match
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
375 except re.error:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
376 raise RouteSyntaxError("Could not add Route: %s (%s)" % (rule, _e()))
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
377
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
378 def match(path):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
379 """ Return an url-argument dictionary. """
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
380 url_args = re_match(path).groupdict()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
381 for name, wildcard_filter in filters:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
382 try:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
383 url_args[name] = wildcard_filter(url_args[name])
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
384 except ValueError:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
385 raise HTTPError(400, 'Path has wrong format.')
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
386 return url_args
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
387
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
388 try:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
389 combined = '%s|(^%s$)' % (self.dynamic[-1][0].pattern, flat_pattern)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
390 self.dynamic[-1] = (re.compile(combined), self.dynamic[-1][1])
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
391 self.dynamic[-1][1].append((match, target))
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
392 except (AssertionError, IndexError): # AssertionError: Too many groups
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
393 self.dynamic.append((re.compile('(^%s$)' % flat_pattern),
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
394 [(match, target)]))
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
395 return match
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
396
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
397 def build(self, _name, *anons, **query):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
398 ''' Build an URL by filling the wildcards in a rule. '''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
399 builder = self.builder.get(_name)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
400 if not builder: raise RouteBuildError("No route with that name.", _name)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
401 try:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
402 for i, value in enumerate(anons): query['anon%d'%i] = value
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
403 url = ''.join([f(query.pop(n)) if n else f for (n,f) in builder])
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
404 return url if not query else url+'?'+urlencode(query)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
405 except KeyError:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
406 raise RouteBuildError('Missing URL argument: %r' % _e().args[0])
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
407
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
408 def match(self, environ):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
409 ''' Return a (target, url_agrs) tuple or raise HTTPError(400/404/405). '''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
410 path, targets, urlargs = environ['PATH_INFO'] or '/', None, {}
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
411 if path in self.static:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
412 targets = self.static[path]
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
413 else:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
414 for combined, rules in self.dynamic:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
415 match = combined.match(path)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
416 if not match: continue
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
417 getargs, targets = rules[match.lastindex - 1]
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
418 urlargs = getargs(path) if getargs else {}
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
419 break
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
420
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
421 if not targets:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
422 raise HTTPError(404, "Not found: " + repr(environ['PATH_INFO']))
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
423 method = environ['REQUEST_METHOD'].upper()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
424 if method in targets:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
425 return targets[method], urlargs
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
426 if method == 'HEAD' and 'GET' in targets:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
427 return targets['GET'], urlargs
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
428 if 'ANY' in targets:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
429 return targets['ANY'], urlargs
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
430 allowed = [verb for verb in targets if verb != 'ANY']
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
431 if 'GET' in allowed and 'HEAD' not in allowed:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
432 allowed.append('HEAD')
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
433 raise HTTPError(405, "Method not allowed.",
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
434 header=[('Allow',",".join(allowed))])
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
435
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
436
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
437 class Route(object):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
438 ''' This class wraps a route callback along with route specific metadata and
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
439 configuration and applies Plugins on demand. It is also responsible for
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
440 turing an URL path rule into a regular expression usable by the Router.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
441 '''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
442
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
443 def __init__(self, app, rule, method, callback, name=None,
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
444 plugins=None, skiplist=None, **config):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
445 #: The application this route is installed to.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
446 self.app = app
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
447 #: The path-rule string (e.g. ``/wiki/:page``).
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
448 self.rule = rule
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
449 #: The HTTP method as a string (e.g. ``GET``).
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
450 self.method = method
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
451 #: The original callback with no plugins applied. Useful for introspection.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
452 self.callback = callback
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
453 #: The name of the route (if specified) or ``None``.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
454 self.name = name or None
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
455 #: A list of route-specific plugins (see :meth:`Bottle.route`).
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
456 self.plugins = plugins or []
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
457 #: A list of plugins to not apply to this route (see :meth:`Bottle.route`).
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
458 self.skiplist = skiplist or []
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
459 #: Additional keyword arguments passed to the :meth:`Bottle.route`
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
460 #: decorator are stored in this dictionary. Used for route-specific
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
461 #: plugin configuration and meta-data.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
462 self.config = ConfigDict(config)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
463
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
464 def __call__(self, *a, **ka):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
465 depr("Some APIs changed to return Route() instances instead of"\
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
466 " callables. Make sure to use the Route.call method and not to"\
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
467 " call Route instances directly.")
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
468 return self.call(*a, **ka)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
469
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
470 @cached_property
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
471 def call(self):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
472 ''' The route callback with all plugins applied. This property is
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
473 created on demand and then cached to speed up subsequent requests.'''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
474 return self._make_callback()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
475
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
476 def reset(self):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
477 ''' Forget any cached values. The next time :attr:`call` is accessed,
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
478 all plugins are re-applied. '''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
479 self.__dict__.pop('call', None)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
480
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
481 def prepare(self):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
482 ''' Do all on-demand work immediately (useful for debugging).'''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
483 self.call
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
484
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
485 @property
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
486 def _context(self):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
487 depr('Switch to Plugin API v2 and access the Route object directly.')
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
488 return dict(rule=self.rule, method=self.method, callback=self.callback,
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
489 name=self.name, app=self.app, config=self.config,
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
490 apply=self.plugins, skip=self.skiplist)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
491
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
492 def all_plugins(self):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
493 ''' Yield all Plugins affecting this route. '''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
494 unique = set()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
495 for p in reversed(self.app.plugins + self.plugins):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
496 if True in self.skiplist: break
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
497 name = getattr(p, 'name', False)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
498 if name and (name in self.skiplist or name in unique): continue
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
499 if p in self.skiplist or type(p) in self.skiplist: continue
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
500 if name: unique.add(name)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
501 yield p
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
502
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
503 def _make_callback(self):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
504 callback = self.callback
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
505 for plugin in self.all_plugins():
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
506 try:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
507 if hasattr(plugin, 'apply'):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
508 api = getattr(plugin, 'api', 1)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
509 context = self if api > 1 else self._context
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
510 callback = plugin.apply(callback, context)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
511 else:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
512 callback = plugin(callback)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
513 except RouteReset: # Try again with changed configuration.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
514 return self._make_callback()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
515 if not callback is self.callback:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
516 update_wrapper(callback, self.callback)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
517 return callback
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
518
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
519 def __repr__(self):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
520 return '<%s %r %r>' % (self.method, self.rule, self.callback)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
521
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
522
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
523
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
524
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
525
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
526
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
527 ###############################################################################
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
528 # Application Object ###########################################################
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
529 ###############################################################################
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
530
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
531
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
532 class Bottle(object):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
533 """ Each Bottle object represents a single, distinct web application and
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
534 consists of routes, callbacks, plugins, resources and configuration.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
535 Instances are callable WSGI applications.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
536
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
537 :param catchall: If true (default), handle all exceptions. Turn off to
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
538 let debugging middleware handle exceptions.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
539 """
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
540
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
541 def __init__(self, catchall=True, autojson=True):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
542 #: If true, most exceptions are caught and returned as :exc:`HTTPError`
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
543 self.catchall = catchall
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
544
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
545 #: A :cls:`ResourceManager` for application files
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
546 self.resources = ResourceManager()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
547
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
548 #: A :cls:`ConfigDict` for app specific configuration.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
549 self.config = ConfigDict()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
550 self.config.autojson = autojson
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
551
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
552 self.routes = [] # List of installed :class:`Route` instances.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
553 self.router = Router() # Maps requests to :class:`Route` instances.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
554 self.error_handler = {}
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
555
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
556 # Core plugins
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
557 self.plugins = [] # List of installed plugins.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
558 self.hooks = HooksPlugin()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
559 self.install(self.hooks)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
560 if self.config.autojson:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
561 self.install(JSONPlugin())
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
562 self.install(TemplatePlugin())
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
563
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
564
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
565 def mount(self, prefix, app, **options):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
566 ''' Mount an application (:class:`Bottle` or plain WSGI) to a specific
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
567 URL prefix. Example::
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
568
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
569 root_app.mount('/admin/', admin_app)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
570
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
571 :param prefix: path prefix or `mount-point`. If it ends in a slash,
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
572 that slash is mandatory.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
573 :param app: an instance of :class:`Bottle` or a WSGI application.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
574
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
575 All other parameters are passed to the underlying :meth:`route` call.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
576 '''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
577 if isinstance(app, basestring):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
578 prefix, app = app, prefix
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
579 depr('Parameter order of Bottle.mount() changed.') # 0.10
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
580
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
581 segments = [p for p in prefix.split('/') if p]
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
582 if not segments: raise ValueError('Empty path prefix.')
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
583 path_depth = len(segments)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
584
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
585 def mountpoint_wrapper():
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
586 try:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
587 request.path_shift(path_depth)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
588 rs = BaseResponse([], 200)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
589 def start_response(status, header):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
590 rs.status = status
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
591 for name, value in header: rs.add_header(name, value)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
592 return rs.body.append
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
593 body = app(request.environ, start_response)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
594 body = itertools.chain(rs.body, body)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
595 return HTTPResponse(body, rs.status_code, rs.headers)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
596 finally:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
597 request.path_shift(-path_depth)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
598
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
599 options.setdefault('skip', True)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
600 options.setdefault('method', 'ANY')
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
601 options.setdefault('mountpoint', {'prefix': prefix, 'target': app})
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
602 options['callback'] = mountpoint_wrapper
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
603
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
604 self.route('/%s/<:re:.*>' % '/'.join(segments), **options)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
605 if not prefix.endswith('/'):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
606 self.route('/' + '/'.join(segments), **options)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
607
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
608 def merge(self, routes):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
609 ''' Merge the routes of another :class:`Bottle` application or a list of
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
610 :class:`Route` objects into this application. The routes keep their
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
611 'owner', meaning that the :data:`Route.app` attribute is not
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
612 changed. '''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
613 if isinstance(routes, Bottle):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
614 routes = routes.routes
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
615 for route in routes:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
616 self.add_route(route)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
617
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
618 def install(self, plugin):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
619 ''' Add a plugin to the list of plugins and prepare it for being
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
620 applied to all routes of this application. A plugin may be a simple
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
621 decorator or an object that implements the :class:`Plugin` API.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
622 '''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
623 if hasattr(plugin, 'setup'): plugin.setup(self)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
624 if not callable(plugin) and not hasattr(plugin, 'apply'):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
625 raise TypeError("Plugins must be callable or implement .apply()")
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
626 self.plugins.append(plugin)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
627 self.reset()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
628 return plugin
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
629
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
630 def uninstall(self, plugin):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
631 ''' Uninstall plugins. Pass an instance to remove a specific plugin, a type
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
632 object to remove all plugins that match that type, a string to remove
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
633 all plugins with a matching ``name`` attribute or ``True`` to remove all
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
634 plugins. Return the list of removed plugins. '''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
635 removed, remove = [], plugin
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
636 for i, plugin in list(enumerate(self.plugins))[::-1]:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
637 if remove is True or remove is plugin or remove is type(plugin) \
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
638 or getattr(plugin, 'name', True) == remove:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
639 removed.append(plugin)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
640 del self.plugins[i]
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
641 if hasattr(plugin, 'close'): plugin.close()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
642 if removed: self.reset()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
643 return removed
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
644
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
645 def run(self, **kwargs):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
646 ''' Calls :func:`run` with the same parameters. '''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
647 run(self, **kwargs)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
648
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
649 def reset(self, route=None):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
650 ''' Reset all routes (force plugins to be re-applied) and clear all
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
651 caches. If an ID or route object is given, only that specific route
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
652 is affected. '''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
653 if route is None: routes = self.routes
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
654 elif isinstance(route, Route): routes = [route]
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
655 else: routes = [self.routes[route]]
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
656 for route in routes: route.reset()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
657 if DEBUG:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
658 for route in routes: route.prepare()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
659 self.hooks.trigger('app_reset')
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
660
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
661 def close(self):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
662 ''' Close the application and all installed plugins. '''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
663 for plugin in self.plugins:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
664 if hasattr(plugin, 'close'): plugin.close()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
665 self.stopped = True
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
666
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
667 def match(self, environ):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
668 """ Search for a matching route and return a (:class:`Route` , urlargs)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
669 tuple. The second value is a dictionary with parameters extracted
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
670 from the URL. Raise :exc:`HTTPError` (404/405) on a non-match."""
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
671 return self.router.match(environ)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
672
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
673 def get_url(self, routename, **kargs):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
674 """ Return a string that matches a named route """
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
675 scriptname = request.environ.get('SCRIPT_NAME', '').strip('/') + '/'
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
676 location = self.router.build(routename, **kargs).lstrip('/')
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
677 return urljoin(urljoin('/', scriptname), location)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
678
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
679 def add_route(self, route):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
680 ''' Add a route object, but do not change the :data:`Route.app`
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
681 attribute.'''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
682 self.routes.append(route)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
683 self.router.add(route.rule, route.method, route, name=route.name)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
684 if DEBUG: route.prepare()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
685
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
686 def route(self, path=None, method='GET', callback=None, name=None,
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
687 apply=None, skip=None, **config):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
688 """ A decorator to bind a function to a request URL. Example::
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
689
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
690 @app.route('/hello/:name')
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
691 def hello(name):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
692 return 'Hello %s' % name
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
693
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
694 The ``:name`` part is a wildcard. See :class:`Router` for syntax
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
695 details.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
696
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
697 :param path: Request path or a list of paths to listen to. If no
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
698 path is specified, it is automatically generated from the
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
699 signature of the function.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
700 :param method: HTTP method (`GET`, `POST`, `PUT`, ...) or a list of
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
701 methods to listen to. (default: `GET`)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
702 :param callback: An optional shortcut to avoid the decorator
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
703 syntax. ``route(..., callback=func)`` equals ``route(...)(func)``
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
704 :param name: The name for this route. (default: None)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
705 :param apply: A decorator or plugin or a list of plugins. These are
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
706 applied to the route callback in addition to installed plugins.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
707 :param skip: A list of plugins, plugin classes or names. Matching
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
708 plugins are not installed to this route. ``True`` skips all.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
709
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
710 Any additional keyword arguments are stored as route-specific
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
711 configuration and passed to plugins (see :meth:`Plugin.apply`).
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
712 """
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
713 if callable(path): path, callback = None, path
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
714 plugins = makelist(apply)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
715 skiplist = makelist(skip)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
716 def decorator(callback):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
717 # TODO: Documentation and tests
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
718 if isinstance(callback, basestring): callback = load(callback)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
719 for rule in makelist(path) or yieldroutes(callback):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
720 for verb in makelist(method):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
721 verb = verb.upper()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
722 route = Route(self, rule, verb, callback, name=name,
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
723 plugins=plugins, skiplist=skiplist, **config)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
724 self.add_route(route)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
725 return callback
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
726 return decorator(callback) if callback else decorator
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
727
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
728 def get(self, path=None, method='GET', **options):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
729 """ Equals :meth:`route`. """
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
730 return self.route(path, method, **options)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
731
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
732 def post(self, path=None, method='POST', **options):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
733 """ Equals :meth:`route` with a ``POST`` method parameter. """
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
734 return self.route(path, method, **options)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
735
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
736 def put(self, path=None, method='PUT', **options):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
737 """ Equals :meth:`route` with a ``PUT`` method parameter. """
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
738 return self.route(path, method, **options)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
739
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
740 def delete(self, path=None, method='DELETE', **options):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
741 """ Equals :meth:`route` with a ``DELETE`` method parameter. """
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
742 return self.route(path, method, **options)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
743
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
744 def error(self, code=500):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
745 """ Decorator: Register an output handler for a HTTP error code"""
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
746 def wrapper(handler):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
747 self.error_handler[int(code)] = handler
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
748 return handler
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
749 return wrapper
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
750
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
751 def hook(self, name):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
752 """ Return a decorator that attaches a callback to a hook. Three hooks
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
753 are currently implemented:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
754
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
755 - before_request: Executed once before each request
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
756 - after_request: Executed once after each request
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
757 - app_reset: Called whenever :meth:`reset` is called.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
758 """
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
759 def wrapper(func):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
760 self.hooks.add(name, func)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
761 return func
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
762 return wrapper
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
763
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
764 def handle(self, path, method='GET'):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
765 """ (deprecated) Execute the first matching route callback and return
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
766 the result. :exc:`HTTPResponse` exceptions are caught and returned.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
767 If :attr:`Bottle.catchall` is true, other exceptions are caught as
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
768 well and returned as :exc:`HTTPError` instances (500).
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
769 """
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
770 depr("This method will change semantics in 0.10. Try to avoid it.")
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
771 if isinstance(path, dict):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
772 return self._handle(path)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
773 return self._handle({'PATH_INFO': path, 'REQUEST_METHOD': method.upper()})
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
774
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
775 def _handle(self, environ):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
776 try:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
777 environ['bottle.app'] = self
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
778 request.bind(environ)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
779 response.bind()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
780 route, args = self.router.match(environ)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
781 environ['route.handle'] = environ['bottle.route'] = route
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
782 environ['route.url_args'] = args
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
783 return route.call(**args)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
784 except HTTPResponse:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
785 return _e()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
786 except RouteReset:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
787 route.reset()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
788 return self._handle(environ)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
789 except (KeyboardInterrupt, SystemExit, MemoryError):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
790 raise
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
791 except Exception:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
792 if not self.catchall: raise
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
793 stacktrace = format_exc()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
794 environ['wsgi.errors'].write(stacktrace)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
795 return HTTPError(500, "Internal Server Error", _e(), stacktrace)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
796
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
797 def _cast(self, out, peek=None):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
798 """ Try to convert the parameter into something WSGI compatible and set
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
799 correct HTTP headers when possible.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
800 Support: False, str, unicode, dict, HTTPResponse, HTTPError, file-like,
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
801 iterable of strings and iterable of unicodes
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
802 """
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
803
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
804 # Empty output is done here
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
805 if not out:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
806 response['Content-Length'] = 0
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
807 return []
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
808 # Join lists of byte or unicode strings. Mixed lists are NOT supported
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
809 if isinstance(out, (tuple, list))\
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
810 and isinstance(out[0], (bytes, unicode)):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
811 out = out[0][0:0].join(out) # b'abc'[0:0] -> b''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
812 # Encode unicode strings
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
813 if isinstance(out, unicode):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
814 out = out.encode(response.charset)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
815 # Byte Strings are just returned
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
816 if isinstance(out, bytes):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
817 response['Content-Length'] = len(out)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
818 return [out]
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
819 # HTTPError or HTTPException (recursive, because they may wrap anything)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
820 # TODO: Handle these explicitly in handle() or make them iterable.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
821 if isinstance(out, HTTPError):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
822 out.apply(response)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
823 out = self.error_handler.get(out.status, repr)(out)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
824 if isinstance(out, HTTPResponse):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
825 depr('Error handlers must not return :exc:`HTTPResponse`.') #0.9
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
826 return self._cast(out)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
827 if isinstance(out, HTTPResponse):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
828 out.apply(response)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
829 return self._cast(out.output)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
830
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
831 # File-like objects.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
832 if hasattr(out, 'read'):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
833 if 'wsgi.file_wrapper' in request.environ:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
834 return request.environ['wsgi.file_wrapper'](out)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
835 elif hasattr(out, 'close') or not hasattr(out, '__iter__'):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
836 return WSGIFileWrapper(out)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
837
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
838 # Handle Iterables. We peek into them to detect their inner type.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
839 try:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
840 out = iter(out)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
841 first = next(out)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
842 while not first:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
843 first = next(out)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
844 except StopIteration:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
845 return self._cast('')
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
846 except HTTPResponse:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
847 first = _e()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
848 except (KeyboardInterrupt, SystemExit, MemoryError):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
849 raise
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
850 except Exception:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
851 if not self.catchall: raise
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
852 first = HTTPError(500, 'Unhandled exception', _e(), format_exc())
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
853
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
854 # These are the inner types allowed in iterator or generator objects.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
855 if isinstance(first, HTTPResponse):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
856 return self._cast(first)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
857 if isinstance(first, bytes):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
858 return itertools.chain([first], out)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
859 if isinstance(first, unicode):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
860 return imap(lambda x: x.encode(response.charset),
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
861 itertools.chain([first], out))
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
862 return self._cast(HTTPError(500, 'Unsupported response type: %s'\
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
863 % type(first)))
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
864
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
865 def wsgi(self, environ, start_response):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
866 """ The bottle WSGI-interface. """
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
867 try:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
868 out = self._cast(self._handle(environ))
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
869 # rfc2616 section 4.3
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
870 if response._status_code in (100, 101, 204, 304)\
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
871 or request.method == 'HEAD':
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
872 if hasattr(out, 'close'): out.close()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
873 out = []
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
874 if isinstance(response._status_line, unicode):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
875 response._status_line = str(response._status_line)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
876 start_response(response._status_line, list(response.iter_headers()))
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
877 return out
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
878 except (KeyboardInterrupt, SystemExit, MemoryError):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
879 raise
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
880 except Exception:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
881 if not self.catchall: raise
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
882 err = '<h1>Critical error while processing request: %s</h1>' \
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
883 % html_escape(environ.get('PATH_INFO', '/'))
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
884 if DEBUG:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
885 err += '<h2>Error:</h2>\n<pre>\n%s\n</pre>\n' \
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
886 '<h2>Traceback:</h2>\n<pre>\n%s\n</pre>\n' \
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
887 % (html_escape(repr(_e())), html_escape(format_exc()))
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
888 environ['wsgi.errors'].write(err)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
889 headers = [('Content-Type', 'text/html; charset=UTF-8')]
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
890 start_response('500 INTERNAL SERVER ERROR', headers)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
891 return [tob(err)]
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
892
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
893 def __call__(self, environ, start_response):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
894 ''' Each instance of :class:'Bottle' is a WSGI application. '''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
895 return self.wsgi(environ, start_response)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
896
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
897
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
898
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
899
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
900
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
901
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
902 ###############################################################################
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
903 # HTTP and WSGI Tools ##########################################################
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
904 ###############################################################################
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
905
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
906
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
907 class BaseRequest(object):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
908 """ A wrapper for WSGI environment dictionaries that adds a lot of
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
909 convenient access methods and properties. Most of them are read-only.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
910
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
911 Adding new attributes to a request actually adds them to the environ
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
912 dictionary (as 'bottle.request.ext.<name>'). This is the recommended
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
913 way to store and access request-specific data.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
914 """
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
915
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
916 __slots__ = ('environ')
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
917
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
918 #: Maximum size of memory buffer for :attr:`body` in bytes.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
919 MEMFILE_MAX = 102400
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
920 #: Maximum number pr GET or POST parameters per request
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
921 MAX_PARAMS = 100
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
922
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
923 def __init__(self, environ=None):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
924 """ Wrap a WSGI environ dictionary. """
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
925 #: The wrapped WSGI environ dictionary. This is the only real attribute.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
926 #: All other attributes actually are read-only properties.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
927 self.environ = {} if environ is None else environ
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
928 self.environ['bottle.request'] = self
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
929
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
930 @DictProperty('environ', 'bottle.app', read_only=True)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
931 def app(self):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
932 ''' Bottle application handling this request. '''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
933 raise RuntimeError('This request is not connected to an application.')
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
934
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
935 @property
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
936 def path(self):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
937 ''' The value of ``PATH_INFO`` with exactly one prefixed slash (to fix
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
938 broken clients and avoid the "empty path" edge case). '''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
939 return '/' + self.environ.get('PATH_INFO','').lstrip('/')
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
940
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
941 @property
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
942 def method(self):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
943 ''' The ``REQUEST_METHOD`` value as an uppercase string. '''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
944 return self.environ.get('REQUEST_METHOD', 'GET').upper()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
945
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
946 @DictProperty('environ', 'bottle.request.headers', read_only=True)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
947 def headers(self):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
948 ''' A :class:`WSGIHeaderDict` that provides case-insensitive access to
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
949 HTTP request headers. '''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
950 return WSGIHeaderDict(self.environ)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
951
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
952 def get_header(self, name, default=None):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
953 ''' Return the value of a request header, or a given default value. '''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
954 return self.headers.get(name, default)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
955
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
956 @DictProperty('environ', 'bottle.request.cookies', read_only=True)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
957 def cookies(self):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
958 """ Cookies parsed into a :class:`FormsDict`. Signed cookies are NOT
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
959 decoded. Use :meth:`get_cookie` if you expect signed cookies. """
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
960 cookies = SimpleCookie(self.environ.get('HTTP_COOKIE',''))
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
961 cookies = list(cookies.values())[:self.MAX_PARAMS]
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
962 return FormsDict((c.key, c.value) for c in cookies)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
963
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
964 def get_cookie(self, key, default=None, secret=None):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
965 """ Return the content of a cookie. To read a `Signed Cookie`, the
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
966 `secret` must match the one used to create the cookie (see
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
967 :meth:`BaseResponse.set_cookie`). If anything goes wrong (missing
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
968 cookie or wrong signature), return a default value. """
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
969 value = self.cookies.get(key)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
970 if secret and value:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
971 dec = cookie_decode(value, secret) # (key, value) tuple or None
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
972 return dec[1] if dec and dec[0] == key else default
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
973 return value or default
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
974
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
975 @DictProperty('environ', 'bottle.request.query', read_only=True)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
976 def query(self):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
977 ''' The :attr:`query_string` parsed into a :class:`FormsDict`. These
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
978 values are sometimes called "URL arguments" or "GET parameters", but
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
979 not to be confused with "URL wildcards" as they are provided by the
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
980 :class:`Router`. '''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
981 pairs = parse_qsl(self.query_string, keep_blank_values=True)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
982 get = self.environ['bottle.get'] = FormsDict()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
983 for key, value in pairs[:self.MAX_PARAMS]:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
984 get[key] = value
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
985 return get
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
986
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
987 @DictProperty('environ', 'bottle.request.forms', read_only=True)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
988 def forms(self):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
989 """ Form values parsed from an `url-encoded` or `multipart/form-data`
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
990 encoded POST or PUT request body. The result is retuned as a
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
991 :class:`FormsDict`. All keys and values are strings. File uploads
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
992 are stored separately in :attr:`files`. """
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
993 forms = FormsDict()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
994 for name, item in self.POST.allitems():
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
995 if not hasattr(item, 'filename'):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
996 forms[name] = item
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
997 return forms
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
998
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
999 @DictProperty('environ', 'bottle.request.params', read_only=True)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1000 def params(self):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1001 """ A :class:`FormsDict` with the combined values of :attr:`query` and
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1002 :attr:`forms`. File uploads are stored in :attr:`files`. """
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1003 params = FormsDict()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1004 for key, value in self.query.allitems():
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1005 params[key] = value
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1006 for key, value in self.forms.allitems():
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1007 params[key] = value
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1008 return params
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1009
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1010 @DictProperty('environ', 'bottle.request.files', read_only=True)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1011 def files(self):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1012 """ File uploads parsed from an `url-encoded` or `multipart/form-data`
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1013 encoded POST or PUT request body. The values are instances of
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1014 :class:`cgi.FieldStorage`. The most important attributes are:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1015
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1016 filename
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1017 The filename, if specified; otherwise None; this is the client
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1018 side filename, *not* the file name on which it is stored (that's
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1019 a temporary file you don't deal with)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1020 file
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1021 The file(-like) object from which you can read the data.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1022 value
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1023 The value as a *string*; for file uploads, this transparently
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1024 reads the file every time you request the value. Do not do this
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1025 on big files.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1026 """
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1027 files = FormsDict()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1028 for name, item in self.POST.allitems():
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1029 if hasattr(item, 'filename'):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1030 files[name] = item
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1031 return files
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1032
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1033 @DictProperty('environ', 'bottle.request.json', read_only=True)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1034 def json(self):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1035 ''' If the ``Content-Type`` header is ``application/json``, this
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1036 property holds the parsed content of the request body. Only requests
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1037 smaller than :attr:`MEMFILE_MAX` are processed to avoid memory
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1038 exhaustion. '''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1039 if 'application/json' in self.environ.get('CONTENT_TYPE', '') \
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1040 and 0 < self.content_length < self.MEMFILE_MAX:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1041 return json_loads(self.body.read(self.MEMFILE_MAX))
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1042 return None
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1043
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1044 @DictProperty('environ', 'bottle.request.body', read_only=True)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1045 def _body(self):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1046 maxread = max(0, self.content_length)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1047 stream = self.environ['wsgi.input']
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1048 body = BytesIO() if maxread < self.MEMFILE_MAX else TemporaryFile(mode='w+b')
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1049 while maxread > 0:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1050 part = stream.read(min(maxread, self.MEMFILE_MAX))
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1051 if not part: break
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1052 body.write(part)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1053 maxread -= len(part)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1054 self.environ['wsgi.input'] = body
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1055 body.seek(0)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1056 return body
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1057
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1058 @property
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1059 def body(self):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1060 """ The HTTP request body as a seek-able file-like object. Depending on
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1061 :attr:`MEMFILE_MAX`, this is either a temporary file or a
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1062 :class:`io.BytesIO` instance. Accessing this property for the first
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1063 time reads and replaces the ``wsgi.input`` environ variable.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1064 Subsequent accesses just do a `seek(0)` on the file object. """
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1065 self._body.seek(0)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1066 return self._body
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1067
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1068 #: An alias for :attr:`query`.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1069 GET = query
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1070
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1071 @DictProperty('environ', 'bottle.request.post', read_only=True)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1072 def POST(self):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1073 """ The values of :attr:`forms` and :attr:`files` combined into a single
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1074 :class:`FormsDict`. Values are either strings (form values) or
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1075 instances of :class:`cgi.FieldStorage` (file uploads).
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1076 """
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1077 post = FormsDict()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1078 safe_env = {'QUERY_STRING':''} # Build a safe environment for cgi
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1079 for key in ('REQUEST_METHOD', 'CONTENT_TYPE', 'CONTENT_LENGTH'):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1080 if key in self.environ: safe_env[key] = self.environ[key]
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1081 if NCTextIOWrapper:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1082 fb = NCTextIOWrapper(self.body, encoding='ISO-8859-1', newline='\n')
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1083 else:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1084 fb = self.body
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1085 data = cgi.FieldStorage(fp=fb, environ=safe_env, keep_blank_values=True)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1086 for item in (data.list or [])[:self.MAX_PARAMS]:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1087 post[item.name] = item if item.filename else item.value
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1088 return post
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1089
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1090 @property
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1091 def COOKIES(self):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1092 ''' Alias for :attr:`cookies` (deprecated). '''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1093 depr('BaseRequest.COOKIES was renamed to BaseRequest.cookies (lowercase).')
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1094 return self.cookies
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1095
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1096 @property
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1097 def url(self):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1098 """ The full request URI including hostname and scheme. If your app
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1099 lives behind a reverse proxy or load balancer and you get confusing
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1100 results, make sure that the ``X-Forwarded-Host`` header is set
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1101 correctly. """
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1102 return self.urlparts.geturl()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1103
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1104 @DictProperty('environ', 'bottle.request.urlparts', read_only=True)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1105 def urlparts(self):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1106 ''' The :attr:`url` string as an :class:`urlparse.SplitResult` tuple.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1107 The tuple contains (scheme, host, path, query_string and fragment),
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1108 but the fragment is always empty because it is not visible to the
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1109 server. '''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1110 env = self.environ
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1111 http = env.get('wsgi.url_scheme', 'http')
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1112 host = env.get('HTTP_X_FORWARDED_HOST') or env.get('HTTP_HOST')
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1113 if not host:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1114 # HTTP 1.1 requires a Host-header. This is for HTTP/1.0 clients.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1115 host = env.get('SERVER_NAME', '127.0.0.1')
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1116 port = env.get('SERVER_PORT')
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1117 if port and port != ('80' if http == 'http' else '443'):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1118 host += ':' + port
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1119 path = urlquote(self.fullpath)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1120 return UrlSplitResult(http, host, path, env.get('QUERY_STRING'), '')
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1121
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1122 @property
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1123 def fullpath(self):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1124 """ Request path including :attr:`script_name` (if present). """
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1125 return urljoin(self.script_name, self.path.lstrip('/'))
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1126
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1127 @property
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1128 def query_string(self):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1129 """ The raw :attr:`query` part of the URL (everything in between ``?``
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1130 and ``#``) as a string. """
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1131 return self.environ.get('QUERY_STRING', '')
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1132
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1133 @property
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1134 def script_name(self):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1135 ''' The initial portion of the URL's `path` that was removed by a higher
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1136 level (server or routing middleware) before the application was
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1137 called. This script path is returned with leading and tailing
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1138 slashes. '''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1139 script_name = self.environ.get('SCRIPT_NAME', '').strip('/')
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1140 return '/' + script_name + '/' if script_name else '/'
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1141
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1142 def path_shift(self, shift=1):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1143 ''' Shift path segments from :attr:`path` to :attr:`script_name` and
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1144 vice versa.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1145
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1146 :param shift: The number of path segments to shift. May be negative
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1147 to change the shift direction. (default: 1)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1148 '''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1149 script = self.environ.get('SCRIPT_NAME','/')
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1150 self['SCRIPT_NAME'], self['PATH_INFO'] = path_shift(script, self.path, shift)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1151
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1152 @property
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1153 def content_length(self):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1154 ''' The request body length as an integer. The client is responsible to
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1155 set this header. Otherwise, the real length of the body is unknown
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1156 and -1 is returned. In this case, :attr:`body` will be empty. '''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1157 return int(self.environ.get('CONTENT_LENGTH') or -1)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1158
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1159 @property
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1160 def is_xhr(self):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1161 ''' True if the request was triggered by a XMLHttpRequest. This only
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1162 works with JavaScript libraries that support the `X-Requested-With`
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1163 header (most of the popular libraries do). '''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1164 requested_with = self.environ.get('HTTP_X_REQUESTED_WITH','')
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1165 return requested_with.lower() == 'xmlhttprequest'
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1166
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1167 @property
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1168 def is_ajax(self):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1169 ''' Alias for :attr:`is_xhr`. "Ajax" is not the right term. '''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1170 return self.is_xhr
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1171
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1172 @property
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1173 def auth(self):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1174 """ HTTP authentication data as a (user, password) tuple. This
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1175 implementation currently supports basic (not digest) authentication
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1176 only. If the authentication happened at a higher level (e.g. in the
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1177 front web-server or a middleware), the password field is None, but
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1178 the user field is looked up from the ``REMOTE_USER`` environ
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1179 variable. On any errors, None is returned. """
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1180 basic = parse_auth(self.environ.get('HTTP_AUTHORIZATION',''))
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1181 if basic: return basic
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1182 ruser = self.environ.get('REMOTE_USER')
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1183 if ruser: return (ruser, None)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1184 return None
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1185
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1186 @property
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1187 def remote_route(self):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1188 """ A list of all IPs that were involved in this request, starting with
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1189 the client IP and followed by zero or more proxies. This does only
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1190 work if all proxies support the ```X-Forwarded-For`` header. Note
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1191 that this information can be forged by malicious clients. """
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1192 proxy = self.environ.get('HTTP_X_FORWARDED_FOR')
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1193 if proxy: return [ip.strip() for ip in proxy.split(',')]
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1194 remote = self.environ.get('REMOTE_ADDR')
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1195 return [remote] if remote else []
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1196
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1197 @property
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1198 def remote_addr(self):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1199 """ The client IP as a string. Note that this information can be forged
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1200 by malicious clients. """
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1201 route = self.remote_route
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1202 return route[0] if route else None
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1203
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1204 def copy(self):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1205 """ Return a new :class:`Request` with a shallow :attr:`environ` copy. """
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1206 return Request(self.environ.copy())
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1207
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1208 def get(self, value, default=None): return self.environ.get(value, default)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1209 def __getitem__(self, key): return self.environ[key]
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1210 def __delitem__(self, key): self[key] = ""; del(self.environ[key])
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1211 def __iter__(self): return iter(self.environ)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1212 def __len__(self): return len(self.environ)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1213 def keys(self): return self.environ.keys()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1214 def __setitem__(self, key, value):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1215 """ Change an environ value and clear all caches that depend on it. """
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1216
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1217 if self.environ.get('bottle.request.readonly'):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1218 raise KeyError('The environ dictionary is read-only.')
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1219
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1220 self.environ[key] = value
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1221 todelete = ()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1222
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1223 if key == 'wsgi.input':
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1224 todelete = ('body', 'forms', 'files', 'params', 'post', 'json')
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1225 elif key == 'QUERY_STRING':
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1226 todelete = ('query', 'params')
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1227 elif key.startswith('HTTP_'):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1228 todelete = ('headers', 'cookies')
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1229
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1230 for key in todelete:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1231 self.environ.pop('bottle.request.'+key, None)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1232
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1233 def __repr__(self):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1234 return '<%s: %s %s>' % (self.__class__.__name__, self.method, self.url)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1235
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1236 def __getattr__(self, name):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1237 ''' Search in self.environ for additional user defined attributes. '''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1238 try:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1239 var = self.environ['bottle.request.ext.%s'%name]
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1240 return var.__get__(self) if hasattr(var, '__get__') else var
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1241 except KeyError:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1242 raise AttributeError('Attribute %r not defined.' % name)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1243
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1244 def __setattr__(self, name, value):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1245 if name == 'environ': return object.__setattr__(self, name, value)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1246 self.environ['bottle.request.ext.%s'%name] = value
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1247
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1248
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1249
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1250
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1251 def _hkey(s):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1252 return s.title().replace('_','-')
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1253
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1254
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1255 class HeaderProperty(object):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1256 def __init__(self, name, reader=None, writer=str, default=''):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1257 self.name, self.default = name, default
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1258 self.reader, self.writer = reader, writer
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1259 self.__doc__ = 'Current value of the %r header.' % name.title()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1260
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1261 def __get__(self, obj, cls):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1262 if obj is None: return self
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1263 value = obj.headers.get(self.name, self.default)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1264 return self.reader(value) if self.reader else value
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1265
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1266 def __set__(self, obj, value):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1267 obj.headers[self.name] = self.writer(value)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1268
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1269 def __delete__(self, obj):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1270 del obj.headers[self.name]
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1271
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1272
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1273 class BaseResponse(object):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1274 """ Storage class for a response body as well as headers and cookies.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1275
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1276 This class does support dict-like case-insensitive item-access to
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1277 headers, but is NOT a dict. Most notably, iterating over a response
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1278 yields parts of the body and not the headers.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1279 """
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1280
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1281 default_status = 200
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1282 default_content_type = 'text/html; charset=UTF-8'
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1283
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1284 # Header blacklist for specific response codes
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1285 # (rfc2616 section 10.2.3 and 10.3.5)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1286 bad_headers = {
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1287 204: set(('Content-Type',)),
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1288 304: set(('Allow', 'Content-Encoding', 'Content-Language',
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1289 'Content-Length', 'Content-Range', 'Content-Type',
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1290 'Content-Md5', 'Last-Modified'))}
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1291
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1292 def __init__(self, body='', status=None, **headers):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1293 self._status_line = None
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1294 self._status_code = None
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1295 self._cookies = None
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1296 self._headers = {'Content-Type': [self.default_content_type]}
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1297 self.body = body
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1298 self.status = status or self.default_status
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1299 if headers:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1300 for name, value in headers.items():
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1301 self[name] = value
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1302
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1303 def copy(self):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1304 ''' Returns a copy of self. '''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1305 copy = Response()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1306 copy.status = self.status
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1307 copy._headers = dict((k, v[:]) for (k, v) in self._headers.items())
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1308 return copy
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1309
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1310 def __iter__(self):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1311 return iter(self.body)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1312
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1313 def close(self):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1314 if hasattr(self.body, 'close'):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1315 self.body.close()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1316
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1317 @property
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1318 def status_line(self):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1319 ''' The HTTP status line as a string (e.g. ``404 Not Found``).'''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1320 return self._status_line
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1321
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1322 @property
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1323 def status_code(self):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1324 ''' The HTTP status code as an integer (e.g. 404).'''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1325 return self._status_code
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1326
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1327 def _set_status(self, status):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1328 if isinstance(status, int):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1329 code, status = status, _HTTP_STATUS_LINES.get(status)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1330 elif ' ' in status:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1331 status = status.strip()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1332 code = int(status.split()[0])
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1333 else:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1334 raise ValueError('String status line without a reason phrase.')
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1335 if not 100 <= code <= 999: raise ValueError('Status code out of range.')
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1336 self._status_code = code
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1337 self._status_line = status or ('%d Unknown' % code)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1338
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1339 def _get_status(self):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1340 return self._status_line
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1341
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1342 status = property(_get_status, _set_status, None,
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1343 ''' A writeable property to change the HTTP response status. It accepts
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1344 either a numeric code (100-999) or a string with a custom reason
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1345 phrase (e.g. "404 Brain not found"). Both :data:`status_line` and
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1346 :data:`status_code` are updated accordingly. The return value is
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1347 always a status string. ''')
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1348 del _get_status, _set_status
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1349
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1350 @property
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1351 def headers(self):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1352 ''' An instance of :class:`HeaderDict`, a case-insensitive dict-like
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1353 view on the response headers. '''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1354 self.__dict__['headers'] = hdict = HeaderDict()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1355 hdict.dict = self._headers
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1356 return hdict
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1357
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1358 def __contains__(self, name): return _hkey(name) in self._headers
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1359 def __delitem__(self, name): del self._headers[_hkey(name)]
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1360 def __getitem__(self, name): return self._headers[_hkey(name)][-1]
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1361 def __setitem__(self, name, value): self._headers[_hkey(name)] = [str(value)]
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1362
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1363 def get_header(self, name, default=None):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1364 ''' Return the value of a previously defined header. If there is no
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1365 header with that name, return a default value. '''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1366 return self._headers.get(_hkey(name), [default])[-1]
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1367
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1368 def set_header(self, name, value, append=False):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1369 ''' Create a new response header, replacing any previously defined
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1370 headers with the same name. '''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1371 if append:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1372 self.add_header(name, value)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1373 else:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1374 self._headers[_hkey(name)] = [str(value)]
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1375
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1376 def add_header(self, name, value):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1377 ''' Add an additional response header, not removing duplicates. '''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1378 self._headers.setdefault(_hkey(name), []).append(str(value))
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1379
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1380 def iter_headers(self):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1381 ''' Yield (header, value) tuples, skipping headers that are not
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1382 allowed with the current response status code. '''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1383 headers = self._headers.items()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1384 bad_headers = self.bad_headers.get(self._status_code)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1385 if bad_headers:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1386 headers = [h for h in headers if h[0] not in bad_headers]
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1387 for name, values in headers:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1388 for value in values:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1389 yield name, value
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1390 if self._cookies:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1391 for c in self._cookies.values():
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1392 yield 'Set-Cookie', c.OutputString()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1393
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1394 def wsgiheader(self):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1395 depr('The wsgiheader method is deprecated. See headerlist.') #0.10
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1396 return self.headerlist
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1397
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1398 @property
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1399 def headerlist(self):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1400 ''' WSGI conform list of (header, value) tuples. '''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1401 return list(self.iter_headers())
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1402
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1403 content_type = HeaderProperty('Content-Type')
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1404 content_length = HeaderProperty('Content-Length', reader=int)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1405
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1406 @property
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1407 def charset(self):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1408 """ Return the charset specified in the content-type header (default: utf8). """
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1409 if 'charset=' in self.content_type:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1410 return self.content_type.split('charset=')[-1].split(';')[0].strip()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1411 return 'UTF-8'
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1412
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1413 @property
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1414 def COOKIES(self):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1415 """ A dict-like SimpleCookie instance. This should not be used directly.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1416 See :meth:`set_cookie`. """
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1417 depr('The COOKIES dict is deprecated. Use `set_cookie()` instead.') # 0.10
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1418 if not self._cookies:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1419 self._cookies = SimpleCookie()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1420 return self._cookies
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1421
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1422 def set_cookie(self, name, value, secret=None, **options):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1423 ''' Create a new cookie or replace an old one. If the `secret` parameter is
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1424 set, create a `Signed Cookie` (described below).
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1425
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1426 :param name: the name of the cookie.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1427 :param value: the value of the cookie.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1428 :param secret: a signature key required for signed cookies.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1429
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1430 Additionally, this method accepts all RFC 2109 attributes that are
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1431 supported by :class:`cookie.Morsel`, including:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1432
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1433 :param max_age: maximum age in seconds. (default: None)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1434 :param expires: a datetime object or UNIX timestamp. (default: None)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1435 :param domain: the domain that is allowed to read the cookie.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1436 (default: current domain)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1437 :param path: limits the cookie to a given path (default: current path)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1438 :param secure: limit the cookie to HTTPS connections (default: off).
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1439 :param httponly: prevents client-side javascript to read this cookie
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1440 (default: off, requires Python 2.6 or newer).
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1441
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1442 If neither `expires` nor `max_age` is set (default), the cookie will
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1443 expire at the end of the browser session (as soon as the browser
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1444 window is closed).
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1445
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1446 Signed cookies may store any pickle-able object and are
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1447 cryptographically signed to prevent manipulation. Keep in mind that
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1448 cookies are limited to 4kb in most browsers.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1449
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1450 Warning: Signed cookies are not encrypted (the client can still see
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1451 the content) and not copy-protected (the client can restore an old
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1452 cookie). The main intention is to make pickling and unpickling
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1453 save, not to store secret information at client side.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1454 '''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1455 if not self._cookies:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1456 self._cookies = SimpleCookie()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1457
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1458 if secret:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1459 value = touni(cookie_encode((name, value), secret))
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1460 elif not isinstance(value, basestring):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1461 raise TypeError('Secret key missing for non-string Cookie.')
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1462
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1463 if len(value) > 4096: raise ValueError('Cookie value to long.')
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1464 self._cookies[name] = value
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1465
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1466 for key, value in options.items():
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1467 if key == 'max_age':
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1468 if isinstance(value, timedelta):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1469 value = value.seconds + value.days * 24 * 3600
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1470 if key == 'expires':
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1471 if isinstance(value, (datedate, datetime)):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1472 value = value.timetuple()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1473 elif isinstance(value, (int, float)):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1474 value = time.gmtime(value)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1475 value = time.strftime("%a, %d %b %Y %H:%M:%S GMT", value)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1476 self._cookies[name][key.replace('_', '-')] = value
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1477
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1478 def delete_cookie(self, key, **kwargs):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1479 ''' Delete a cookie. Be sure to use the same `domain` and `path`
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1480 settings as used to create the cookie. '''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1481 kwargs['max_age'] = -1
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1482 kwargs['expires'] = 0
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1483 self.set_cookie(key, '', **kwargs)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1484
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1485 def __repr__(self):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1486 out = ''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1487 for name, value in self.headerlist:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1488 out += '%s: %s\n' % (name.title(), value.strip())
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1489 return out
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1490
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1491 #: Thread-local storage for :class:`LocalRequest` and :class:`LocalResponse`
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1492 #: attributes.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1493 _lctx = threading.local()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1494
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1495 def local_property(name):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1496 def fget(self):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1497 try:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1498 return getattr(_lctx, name)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1499 except AttributeError:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1500 raise RuntimeError("Request context not initialized.")
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1501 def fset(self, value): setattr(_lctx, name, value)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1502 def fdel(self): delattr(_lctx, name)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1503 return property(fget, fset, fdel,
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1504 'Thread-local property stored in :data:`_lctx.%s`' % name)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1505
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1506
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1507 class LocalRequest(BaseRequest):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1508 ''' A thread-local subclass of :class:`BaseRequest` with a different
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1509 set of attribues for each thread. There is usually only one global
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1510 instance of this class (:data:`request`). If accessed during a
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1511 request/response cycle, this instance always refers to the *current*
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1512 request (even on a multithreaded server). '''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1513 bind = BaseRequest.__init__
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1514 environ = local_property('request_environ')
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1515
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1516
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1517 class LocalResponse(BaseResponse):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1518 ''' A thread-local subclass of :class:`BaseResponse` with a different
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1519 set of attribues for each thread. There is usually only one global
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1520 instance of this class (:data:`response`). Its attributes are used
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1521 to build the HTTP response at the end of the request/response cycle.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1522 '''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1523 bind = BaseResponse.__init__
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1524 _status_line = local_property('response_status_line')
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1525 _status_code = local_property('response_status_code')
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1526 _cookies = local_property('response_cookies')
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1527 _headers = local_property('response_headers')
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1528 body = local_property('response_body')
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1529
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1530 Response = LocalResponse # BC 0.9
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1531 Request = LocalRequest # BC 0.9
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1532
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1533
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1534
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1535
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1536
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1537
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1538 ###############################################################################
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1539 # Plugins ######################################################################
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1540 ###############################################################################
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1541
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1542 class PluginError(BottleException): pass
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1543
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1544 class JSONPlugin(object):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1545 name = 'json'
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1546 api = 2
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1547
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1548 def __init__(self, json_dumps=json_dumps):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1549 self.json_dumps = json_dumps
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1550
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1551 def apply(self, callback, context):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1552 dumps = self.json_dumps
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1553 if not dumps: return callback
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1554 def wrapper(*a, **ka):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1555 rv = callback(*a, **ka)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1556 if isinstance(rv, dict):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1557 #Attempt to serialize, raises exception on failure
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1558 json_response = dumps(rv)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1559 #Set content type only if serialization succesful
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1560 response.content_type = 'application/json'
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1561 return json_response
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1562 return rv
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1563 return wrapper
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1564
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1565
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1566 class HooksPlugin(object):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1567 name = 'hooks'
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1568 api = 2
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1569
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1570 _names = 'before_request', 'after_request', 'app_reset'
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1571
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1572 def __init__(self):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1573 self.hooks = dict((name, []) for name in self._names)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1574 self.app = None
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1575
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1576 def _empty(self):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1577 return not (self.hooks['before_request'] or self.hooks['after_request'])
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1578
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1579 def setup(self, app):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1580 self.app = app
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1581
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1582 def add(self, name, func):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1583 ''' Attach a callback to a hook. '''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1584 was_empty = self._empty()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1585 self.hooks.setdefault(name, []).append(func)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1586 if self.app and was_empty and not self._empty(): self.app.reset()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1587
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1588 def remove(self, name, func):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1589 ''' Remove a callback from a hook. '''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1590 was_empty = self._empty()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1591 if name in self.hooks and func in self.hooks[name]:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1592 self.hooks[name].remove(func)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1593 if self.app and not was_empty and self._empty(): self.app.reset()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1594
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1595 def trigger(self, name, *a, **ka):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1596 ''' Trigger a hook and return a list of results. '''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1597 hooks = self.hooks[name]
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1598 if ka.pop('reversed', False): hooks = hooks[::-1]
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1599 return [hook(*a, **ka) for hook in hooks]
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1600
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1601 def apply(self, callback, context):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1602 if self._empty(): return callback
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1603 def wrapper(*a, **ka):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1604 self.trigger('before_request')
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1605 rv = callback(*a, **ka)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1606 self.trigger('after_request', reversed=True)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1607 return rv
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1608 return wrapper
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1609
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1610
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1611 class TemplatePlugin(object):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1612 ''' This plugin applies the :func:`view` decorator to all routes with a
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1613 `template` config parameter. If the parameter is a tuple, the second
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1614 element must be a dict with additional options (e.g. `template_engine`)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1615 or default variables for the template. '''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1616 name = 'template'
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1617 api = 2
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1618
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1619 def apply(self, callback, route):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1620 conf = route.config.get('template')
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1621 if isinstance(conf, (tuple, list)) and len(conf) == 2:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1622 return view(conf[0], **conf[1])(callback)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1623 elif isinstance(conf, str) and 'template_opts' in route.config:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1624 depr('The `template_opts` parameter is deprecated.') #0.9
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1625 return view(conf, **route.config['template_opts'])(callback)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1626 elif isinstance(conf, str):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1627 return view(conf)(callback)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1628 else:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1629 return callback
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1630
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1631
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1632 #: Not a plugin, but part of the plugin API. TODO: Find a better place.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1633 class _ImportRedirect(object):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1634 def __init__(self, name, impmask):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1635 ''' Create a virtual package that redirects imports (see PEP 302). '''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1636 self.name = name
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1637 self.impmask = impmask
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1638 self.module = sys.modules.setdefault(name, imp.new_module(name))
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1639 self.module.__dict__.update({'__file__': __file__, '__path__': [],
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1640 '__all__': [], '__loader__': self})
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1641 sys.meta_path.append(self)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1642
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1643 def find_module(self, fullname, path=None):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1644 if '.' not in fullname: return
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1645 packname, modname = fullname.rsplit('.', 1)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1646 if packname != self.name: return
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1647 return self
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1648
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1649 def load_module(self, fullname):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1650 if fullname in sys.modules: return sys.modules[fullname]
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1651 packname, modname = fullname.rsplit('.', 1)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1652 realname = self.impmask % modname
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1653 __import__(realname)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1654 module = sys.modules[fullname] = sys.modules[realname]
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1655 setattr(self.module, modname, module)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1656 module.__loader__ = self
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1657 return module
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1658
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1659
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1660
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1661
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1662
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1663
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1664 ###############################################################################
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1665 # Common Utilities #############################################################
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1666 ###############################################################################
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1667
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1668
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1669 class MultiDict(DictMixin):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1670 """ This dict stores multiple values per key, but behaves exactly like a
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1671 normal dict in that it returns only the newest value for any given key.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1672 There are special methods available to access the full list of values.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1673 """
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1674
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1675 def __init__(self, *a, **k):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1676 self.dict = dict((k, [v]) for (k, v) in dict(*a, **k).items())
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1677
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1678 def __len__(self): return len(self.dict)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1679 def __iter__(self): return iter(self.dict)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1680 def __contains__(self, key): return key in self.dict
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1681 def __delitem__(self, key): del self.dict[key]
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1682 def __getitem__(self, key): return self.dict[key][-1]
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1683 def __setitem__(self, key, value): self.append(key, value)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1684 def keys(self): return self.dict.keys()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1685
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1686 if py3k:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1687 def values(self): return (v[-1] for v in self.dict.values())
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1688 def items(self): return ((k, v[-1]) for k, v in self.dict.items())
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1689 def allitems(self):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1690 return ((k, v) for k, vl in self.dict.items() for v in vl)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1691 iterkeys = keys
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1692 itervalues = values
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1693 iteritems = items
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1694 iterallitems = allitems
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1695
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1696 else:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1697 def values(self): return [v[-1] for v in self.dict.values()]
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1698 def items(self): return [(k, v[-1]) for k, v in self.dict.items()]
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1699 def iterkeys(self): return self.dict.iterkeys()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1700 def itervalues(self): return (v[-1] for v in self.dict.itervalues())
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1701 def iteritems(self):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1702 return ((k, v[-1]) for k, v in self.dict.iteritems())
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1703 def iterallitems(self):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1704 return ((k, v) for k, vl in self.dict.iteritems() for v in vl)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1705 def allitems(self):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1706 return [(k, v) for k, vl in self.dict.iteritems() for v in vl]
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1707
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1708 def get(self, key, default=None, index=-1, type=None):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1709 ''' Return the most recent value for a key.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1710
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1711 :param default: The default value to be returned if the key is not
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1712 present or the type conversion fails.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1713 :param index: An index for the list of available values.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1714 :param type: If defined, this callable is used to cast the value
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1715 into a specific type. Exception are suppressed and result in
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1716 the default value to be returned.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1717 '''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1718 try:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1719 val = self.dict[key][index]
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1720 return type(val) if type else val
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1721 except Exception:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1722 pass
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1723 return default
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1724
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1725 def append(self, key, value):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1726 ''' Add a new value to the list of values for this key. '''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1727 self.dict.setdefault(key, []).append(value)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1728
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1729 def replace(self, key, value):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1730 ''' Replace the list of values with a single value. '''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1731 self.dict[key] = [value]
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1732
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1733 def getall(self, key):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1734 ''' Return a (possibly empty) list of values for a key. '''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1735 return self.dict.get(key) or []
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1736
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1737 #: Aliases for WTForms to mimic other multi-dict APIs (Django)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1738 getone = get
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1739 getlist = getall
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1740
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1741
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1742
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1743 class FormsDict(MultiDict):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1744 ''' This :class:`MultiDict` subclass is used to store request form data.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1745 Additionally to the normal dict-like item access methods (which return
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1746 unmodified data as native strings), this container also supports
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1747 attribute-like access to its values. Attributes are automatically de-
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1748 or recoded to match :attr:`input_encoding` (default: 'utf8'). Missing
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1749 attributes default to an empty string. '''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1750
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1751 #: Encoding used for attribute values.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1752 input_encoding = 'utf8'
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1753 #: If true (default), unicode strings are first encoded with `latin1`
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1754 #: and then decoded to match :attr:`input_encoding`.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1755 recode_unicode = True
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1756
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1757 def _fix(self, s, encoding=None):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1758 if isinstance(s, unicode) and self.recode_unicode: # Python 3 WSGI
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1759 s = s.encode('latin1')
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1760 if isinstance(s, bytes): # Python 2 WSGI
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1761 return s.decode(encoding or self.input_encoding)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1762 return s
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1763
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1764 def decode(self, encoding=None):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1765 ''' Returns a copy with all keys and values de- or recoded to match
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1766 :attr:`input_encoding`. Some libraries (e.g. WTForms) want a
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1767 unicode dictionary. '''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1768 copy = FormsDict()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1769 enc = copy.input_encoding = encoding or self.input_encoding
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1770 copy.recode_unicode = False
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1771 for key, value in self.allitems():
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1772 copy.append(self._fix(key, enc), self._fix(value, enc))
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1773 return copy
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1774
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1775 def getunicode(self, name, default=None, encoding=None):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1776 try:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1777 return self._fix(self[name], encoding)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1778 except (UnicodeError, KeyError):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1779 return default
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1780
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1781 def __getattr__(self, name, default=unicode()):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1782 # Without this guard, pickle generates a cryptic TypeError:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1783 if name.startswith('__') and name.endswith('__'):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1784 return super(FormsDict, self).__getattr__(name)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1785 return self.getunicode(name, default=default)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1786
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1787
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1788 class HeaderDict(MultiDict):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1789 """ A case-insensitive version of :class:`MultiDict` that defaults to
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1790 replace the old value instead of appending it. """
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1791
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1792 def __init__(self, *a, **ka):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1793 self.dict = {}
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1794 if a or ka: self.update(*a, **ka)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1795
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1796 def __contains__(self, key): return _hkey(key) in self.dict
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1797 def __delitem__(self, key): del self.dict[_hkey(key)]
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1798 def __getitem__(self, key): return self.dict[_hkey(key)][-1]
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1799 def __setitem__(self, key, value): self.dict[_hkey(key)] = [str(value)]
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1800 def append(self, key, value):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1801 self.dict.setdefault(_hkey(key), []).append(str(value))
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1802 def replace(self, key, value): self.dict[_hkey(key)] = [str(value)]
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1803 def getall(self, key): return self.dict.get(_hkey(key)) or []
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1804 def get(self, key, default=None, index=-1):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1805 return MultiDict.get(self, _hkey(key), default, index)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1806 def filter(self, names):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1807 for name in [_hkey(n) for n in names]:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1808 if name in self.dict:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1809 del self.dict[name]
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1810
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1811
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1812 class WSGIHeaderDict(DictMixin):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1813 ''' This dict-like class wraps a WSGI environ dict and provides convenient
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1814 access to HTTP_* fields. Keys and values are native strings
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1815 (2.x bytes or 3.x unicode) and keys are case-insensitive. If the WSGI
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1816 environment contains non-native string values, these are de- or encoded
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1817 using a lossless 'latin1' character set.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1818
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1819 The API will remain stable even on changes to the relevant PEPs.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1820 Currently PEP 333, 444 and 3333 are supported. (PEP 444 is the only one
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1821 that uses non-native strings.)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1822 '''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1823 #: List of keys that do not have a 'HTTP_' prefix.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1824 cgikeys = ('CONTENT_TYPE', 'CONTENT_LENGTH')
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1825
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1826 def __init__(self, environ):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1827 self.environ = environ
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1828
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1829 def _ekey(self, key):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1830 ''' Translate header field name to CGI/WSGI environ key. '''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1831 key = key.replace('-','_').upper()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1832 if key in self.cgikeys:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1833 return key
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1834 return 'HTTP_' + key
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1835
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1836 def raw(self, key, default=None):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1837 ''' Return the header value as is (may be bytes or unicode). '''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1838 return self.environ.get(self._ekey(key), default)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1839
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1840 def __getitem__(self, key):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1841 return tonat(self.environ[self._ekey(key)], 'latin1')
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1842
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1843 def __setitem__(self, key, value):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1844 raise TypeError("%s is read-only." % self.__class__)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1845
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1846 def __delitem__(self, key):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1847 raise TypeError("%s is read-only." % self.__class__)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1848
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1849 def __iter__(self):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1850 for key in self.environ:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1851 if key[:5] == 'HTTP_':
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1852 yield key[5:].replace('_', '-').title()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1853 elif key in self.cgikeys:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1854 yield key.replace('_', '-').title()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1855
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1856 def keys(self): return [x for x in self]
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1857 def __len__(self): return len(self.keys())
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1858 def __contains__(self, key): return self._ekey(key) in self.environ
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1859
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1860
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1861 class ConfigDict(dict):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1862 ''' A dict-subclass with some extras: You can access keys like attributes.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1863 Uppercase attributes create new ConfigDicts and act as name-spaces.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1864 Other missing attributes return None. Calling a ConfigDict updates its
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1865 values and returns itself.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1866
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1867 >>> cfg = ConfigDict()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1868 >>> cfg.Namespace.value = 5
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1869 >>> cfg.OtherNamespace(a=1, b=2)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1870 >>> cfg
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1871 {'Namespace': {'value': 5}, 'OtherNamespace': {'a': 1, 'b': 2}}
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1872 '''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1873
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1874 def __getattr__(self, key):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1875 if key not in self and key[0].isupper():
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1876 self[key] = ConfigDict()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1877 return self.get(key)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1878
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1879 def __setattr__(self, key, value):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1880 if hasattr(dict, key):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1881 raise AttributeError('Read-only attribute.')
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1882 if key in self and self[key] and isinstance(self[key], ConfigDict):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1883 raise AttributeError('Non-empty namespace attribute.')
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1884 self[key] = value
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1885
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1886 def __delattr__(self, key):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1887 if key in self: del self[key]
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1888
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1889 def __call__(self, *a, **ka):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1890 for key, value in dict(*a, **ka).items(): setattr(self, key, value)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1891 return self
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1892
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1893
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1894 class AppStack(list):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1895 """ A stack-like list. Calling it returns the head of the stack. """
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1896
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1897 def __call__(self):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1898 """ Return the current default application. """
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1899 return self[-1]
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1900
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1901 def push(self, value=None):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1902 """ Add a new :class:`Bottle` instance to the stack """
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1903 if not isinstance(value, Bottle):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1904 value = Bottle()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1905 self.append(value)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1906 return value
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1907
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1908
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1909 class WSGIFileWrapper(object):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1910
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1911 def __init__(self, fp, buffer_size=1024*64):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1912 self.fp, self.buffer_size = fp, buffer_size
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1913 for attr in ('fileno', 'close', 'read', 'readlines', 'tell', 'seek'):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1914 if hasattr(fp, attr): setattr(self, attr, getattr(fp, attr))
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1915
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1916 def __iter__(self):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1917 buff, read = self.buffer_size, self.read
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1918 while True:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1919 part = read(buff)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1920 if not part: return
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1921 yield part
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1922
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1923
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1924 class ResourceManager(object):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1925 ''' This class manages a list of search paths and helps to find and open
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1926 aplication-bound resources (files).
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1927
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1928 :param base: default value for same-named :meth:`add_path` parameter.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1929 :param opener: callable used to open resources.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1930 :param cachemode: controls which lookups are cached. One of 'all',
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1931 'found' or 'none'.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1932 '''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1933
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1934 def __init__(self, base='./', opener=open, cachemode='all'):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1935 self.opener = open
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1936 self.base = base
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1937 self.cachemode = cachemode
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1938
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1939 #: A list of search paths. See :meth:`add_path` for details.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1940 self.path = []
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1941 #: A cache for resolved paths. `res.cache.clear()`` clears the cache.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1942 self.cache = {}
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1943
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1944 def add_path(self, path, base=None, index=None, create=False):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1945 ''' Add a new path to the list of search paths. Return False if it does
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1946 not exist.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1947
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1948 :param path: The new search path. Relative paths are turned into an
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1949 absolute and normalized form. If the path looks like a file (not
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1950 ending in `/`), the filename is stripped off.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1951 :param base: Path used to absolutize relative search paths.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1952 Defaults to `:attr:base` which defaults to ``./``.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1953 :param index: Position within the list of search paths. Defaults to
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1954 last index (appends to the list).
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1955 :param create: Create non-existent search paths. Off by default.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1956
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1957 The `base` parameter makes it easy to reference files installed
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1958 along with a python module or package::
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1959
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1960 res.add_path('./resources/', __file__)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1961 '''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1962 base = os.path.abspath(os.path.dirname(base or self.base))
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1963 path = os.path.abspath(os.path.join(base, os.path.dirname(path)))
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1964 path += os.sep
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1965 if path in self.path:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1966 self.path.remove(path)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1967 if create and not os.path.isdir(path):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1968 os.mkdirs(path)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1969 if index is None:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1970 self.path.append(path)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1971 else:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1972 self.path.insert(index, path)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1973 self.cache.clear()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1974
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1975 def __iter__(self):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1976 ''' Iterate over all existing files in all registered paths. '''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1977 search = self.path[:]
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1978 while search:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1979 path = search.pop()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1980 if not os.path.isdir(path): continue
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1981 for name in os.listdir(path):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1982 full = os.path.join(path, name)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1983 if os.path.isdir(full): search.append(full)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1984 else: yield full
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1985
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1986 def lookup(self, name):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1987 ''' Search for a resource and return an absolute file path, or `None`.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1988
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1989 The :attr:`path` list is searched in order. The first match is
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1990 returend. Symlinks are followed. The result is cached to speed up
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1991 future lookups. '''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1992 if name not in self.cache or DEBUG:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1993 for path in self.path:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1994 fpath = os.path.join(path, name)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1995 if os.path.isfile(fpath):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1996 if self.cachemode in ('all', 'found'):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1997 self.cache[name] = fpath
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1998 return fpath
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
1999 if self.cachemode == 'all':
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2000 self.cache[name] = None
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2001 return self.cache[name]
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2002
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2003 def open(self, name, mode='r', *args, **kwargs):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2004 ''' Find a resource and return a file object, or raise IOError. '''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2005 fname = self.lookup(name)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2006 if not fname: raise IOError("Resource %r not found." % name)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2007 return self.opener(name, mode=mode, *args, **kwargs)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2008
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2009
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2010
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2011
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2012
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2013
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2014 ###############################################################################
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2015 # Application Helper ###########################################################
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2016 ###############################################################################
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2017
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2018
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2019 def abort(code=500, text='Unknown Error: Application stopped.'):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2020 """ Aborts execution and causes a HTTP error. """
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2021 raise HTTPError(code, text)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2022
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2023
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2024 def redirect(url, code=None):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2025 """ Aborts execution and causes a 303 or 302 redirect, depending on
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2026 the HTTP protocol version. """
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2027 if code is None:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2028 code = 303 if request.get('SERVER_PROTOCOL') == "HTTP/1.1" else 302
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2029 location = urljoin(request.url, url)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2030 raise HTTPResponse("", status=code, header=dict(Location=location))
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2031
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2032
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2033 def _file_iter_range(fp, offset, bytes, maxread=1024*1024):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2034 ''' Yield chunks from a range in a file. No chunk is bigger than maxread.'''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2035 fp.seek(offset)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2036 while bytes > 0:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2037 part = fp.read(min(bytes, maxread))
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2038 if not part: break
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2039 bytes -= len(part)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2040 yield part
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2041
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2042
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2043 def static_file(filename, root, mimetype='auto', download=False):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2044 """ Open a file in a safe way and return :exc:`HTTPResponse` with status
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2045 code 200, 305, 401 or 404. Set Content-Type, Content-Encoding,
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2046 Content-Length and Last-Modified header. Obey If-Modified-Since header
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2047 and HEAD requests.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2048 """
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2049 root = os.path.abspath(root) + os.sep
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2050 filename = os.path.abspath(os.path.join(root, filename.strip('/\\')))
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2051 header = dict()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2052
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2053 if not filename.startswith(root):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2054 return HTTPError(403, "Access denied.")
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2055 if not os.path.exists(filename) or not os.path.isfile(filename):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2056 return HTTPError(404, "File does not exist.")
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2057 if not os.access(filename, os.R_OK):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2058 return HTTPError(403, "You do not have permission to access this file.")
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2059
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2060 if mimetype == 'auto':
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2061 mimetype, encoding = mimetypes.guess_type(filename)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2062 if mimetype: header['Content-Type'] = mimetype
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2063 if encoding: header['Content-Encoding'] = encoding
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2064 elif mimetype:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2065 header['Content-Type'] = mimetype
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2066
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2067 if download:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2068 download = os.path.basename(filename if download == True else download)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2069 header['Content-Disposition'] = 'attachment; filename="%s"' % download
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2070
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2071 stats = os.stat(filename)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2072 header['Content-Length'] = clen = stats.st_size
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2073 lm = time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(stats.st_mtime))
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2074 header['Last-Modified'] = lm
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2075
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2076 ims = request.environ.get('HTTP_IF_MODIFIED_SINCE')
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2077 if ims:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2078 ims = parse_date(ims.split(";")[0].strip())
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2079 if ims is not None and ims >= int(stats.st_mtime):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2080 header['Date'] = time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime())
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2081 return HTTPResponse(status=304, header=header)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2082
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2083 body = '' if request.method == 'HEAD' else open(filename, 'rb')
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2084
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2085 header["Accept-Ranges"] = "bytes"
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2086 ranges = request.environ.get('HTTP_RANGE')
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2087 if 'HTTP_RANGE' in request.environ:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2088 ranges = list(parse_range_header(request.environ['HTTP_RANGE'], clen))
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2089 if not ranges:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2090 return HTTPError(416, "Requested Range Not Satisfiable")
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2091 offset, end = ranges[0]
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2092 header["Content-Range"] = "bytes %d-%d/%d" % (offset, end-1, clen)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2093 header["Content-Length"] = str(end-offset)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2094 if body: body = _file_iter_range(body, offset, end-offset)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2095 return HTTPResponse(body, header=header, status=206)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2096 return HTTPResponse(body, header=header)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2097
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2098
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2099
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2100
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2101
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2102
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2103 ###############################################################################
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2104 # HTTP Utilities and MISC (TODO) ###############################################
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2105 ###############################################################################
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2106
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2107
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2108 def debug(mode=True):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2109 """ Change the debug level.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2110 There is only one debug level supported at the moment."""
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2111 global DEBUG
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2112 DEBUG = bool(mode)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2113
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2114
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2115 def parse_date(ims):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2116 """ Parse rfc1123, rfc850 and asctime timestamps and return UTC epoch. """
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2117 try:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2118 ts = email.utils.parsedate_tz(ims)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2119 return time.mktime(ts[:8] + (0,)) - (ts[9] or 0) - time.timezone
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2120 except (TypeError, ValueError, IndexError, OverflowError):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2121 return None
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2122
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2123
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2124 def parse_auth(header):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2125 """ Parse rfc2617 HTTP authentication header string (basic) and return (user,pass) tuple or None"""
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2126 try:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2127 method, data = header.split(None, 1)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2128 if method.lower() == 'basic':
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2129 user, pwd = touni(base64.b64decode(tob(data))).split(':',1)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2130 return user, pwd
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2131 except (KeyError, ValueError):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2132 return None
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2133
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2134 def parse_range_header(header, maxlen=0):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2135 ''' Yield (start, end) ranges parsed from a HTTP Range header. Skip
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2136 unsatisfiable ranges. The end index is non-inclusive.'''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2137 if not header or header[:6] != 'bytes=': return
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2138 ranges = [r.split('-', 1) for r in header[6:].split(',') if '-' in r]
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2139 for start, end in ranges:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2140 try:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2141 if not start: # bytes=-100 -> last 100 bytes
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2142 start, end = max(0, maxlen-int(end)), maxlen
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2143 elif not end: # bytes=100- -> all but the first 99 bytes
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2144 start, end = int(start), maxlen
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2145 else: # bytes=100-200 -> bytes 100-200 (inclusive)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2146 start, end = int(start), min(int(end)+1, maxlen)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2147 if 0 <= start < end <= maxlen:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2148 yield start, end
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2149 except ValueError:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2150 pass
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2151
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2152 def _lscmp(a, b):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2153 ''' Compares two strings in a cryptographically safe way:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2154 Runtime is not affected by length of common prefix. '''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2155 return not sum(0 if x==y else 1 for x, y in zip(a, b)) and len(a) == len(b)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2156
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2157
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2158 def cookie_encode(data, key):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2159 ''' Encode and sign a pickle-able object. Return a (byte) string '''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2160 msg = base64.b64encode(pickle.dumps(data, -1))
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2161 sig = base64.b64encode(hmac.new(tob(key), msg).digest())
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2162 return tob('!') + sig + tob('?') + msg
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2163
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2164
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2165 def cookie_decode(data, key):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2166 ''' Verify and decode an encoded string. Return an object or None.'''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2167 data = tob(data)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2168 if cookie_is_encoded(data):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2169 sig, msg = data.split(tob('?'), 1)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2170 if _lscmp(sig[1:], base64.b64encode(hmac.new(tob(key), msg).digest())):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2171 return pickle.loads(base64.b64decode(msg))
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2172 return None
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2173
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2174
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2175 def cookie_is_encoded(data):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2176 ''' Return True if the argument looks like a encoded cookie.'''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2177 return bool(data.startswith(tob('!')) and tob('?') in data)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2178
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2179
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2180 def html_escape(string):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2181 ''' Escape HTML special characters ``&<>`` and quotes ``'"``. '''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2182 return string.replace('&','&amp;').replace('<','&lt;').replace('>','&gt;')\
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2183 .replace('"','&quot;').replace("'",'&#039;')
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2184
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2185
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2186 def html_quote(string):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2187 ''' Escape and quote a string to be used as an HTTP attribute.'''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2188 return '"%s"' % html_escape(string).replace('\n','%#10;')\
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2189 .replace('\r','&#13;').replace('\t','&#9;')
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2190
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2191
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2192 def yieldroutes(func):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2193 """ Return a generator for routes that match the signature (name, args)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2194 of the func parameter. This may yield more than one route if the function
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2195 takes optional keyword arguments. The output is best described by example::
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2196
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2197 a() -> '/a'
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2198 b(x, y) -> '/b/:x/:y'
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2199 c(x, y=5) -> '/c/:x' and '/c/:x/:y'
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2200 d(x=5, y=6) -> '/d' and '/d/:x' and '/d/:x/:y'
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2201 """
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2202 import inspect # Expensive module. Only import if necessary.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2203 path = '/' + func.__name__.replace('__','/').lstrip('/')
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2204 spec = inspect.getargspec(func)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2205 argc = len(spec[0]) - len(spec[3] or [])
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2206 path += ('/:%s' * argc) % tuple(spec[0][:argc])
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2207 yield path
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2208 for arg in spec[0][argc:]:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2209 path += '/:%s' % arg
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2210 yield path
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2211
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2212
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2213 def path_shift(script_name, path_info, shift=1):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2214 ''' Shift path fragments from PATH_INFO to SCRIPT_NAME and vice versa.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2215
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2216 :return: The modified paths.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2217 :param script_name: The SCRIPT_NAME path.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2218 :param script_name: The PATH_INFO path.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2219 :param shift: The number of path fragments to shift. May be negative to
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2220 change the shift direction. (default: 1)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2221 '''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2222 if shift == 0: return script_name, path_info
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2223 pathlist = path_info.strip('/').split('/')
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2224 scriptlist = script_name.strip('/').split('/')
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2225 if pathlist and pathlist[0] == '': pathlist = []
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2226 if scriptlist and scriptlist[0] == '': scriptlist = []
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2227 if shift > 0 and shift <= len(pathlist):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2228 moved = pathlist[:shift]
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2229 scriptlist = scriptlist + moved
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2230 pathlist = pathlist[shift:]
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2231 elif shift < 0 and shift >= -len(scriptlist):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2232 moved = scriptlist[shift:]
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2233 pathlist = moved + pathlist
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2234 scriptlist = scriptlist[:shift]
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2235 else:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2236 empty = 'SCRIPT_NAME' if shift < 0 else 'PATH_INFO'
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2237 raise AssertionError("Cannot shift. Nothing left from %s" % empty)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2238 new_script_name = '/' + '/'.join(scriptlist)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2239 new_path_info = '/' + '/'.join(pathlist)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2240 if path_info.endswith('/') and pathlist: new_path_info += '/'
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2241 return new_script_name, new_path_info
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2242
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2243
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2244 def validate(**vkargs):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2245 """
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2246 Validates and manipulates keyword arguments by user defined callables.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2247 Handles ValueError and missing arguments by raising HTTPError(403).
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2248 """
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2249 depr('Use route wildcard filters instead.')
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2250 def decorator(func):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2251 @functools.wraps(func)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2252 def wrapper(*args, **kargs):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2253 for key, value in vkargs.items():
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2254 if key not in kargs:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2255 abort(403, 'Missing parameter: %s' % key)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2256 try:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2257 kargs[key] = value(kargs[key])
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2258 except ValueError:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2259 abort(403, 'Wrong parameter format for: %s' % key)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2260 return func(*args, **kargs)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2261 return wrapper
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2262 return decorator
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2263
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2264
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2265 def auth_basic(check, realm="private", text="Access denied"):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2266 ''' Callback decorator to require HTTP auth (basic).
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2267 TODO: Add route(check_auth=...) parameter. '''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2268 def decorator(func):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2269 def wrapper(*a, **ka):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2270 user, password = request.auth or (None, None)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2271 if user is None or not check(user, password):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2272 response.headers['WWW-Authenticate'] = 'Basic realm="%s"' % realm
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2273 return HTTPError(401, text)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2274 return func(*a, **ka)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2275 return wrapper
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2276 return decorator
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2277
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2278
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2279 # Shortcuts for common Bottle methods.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2280 # They all refer to the current default application.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2281
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2282 def make_default_app_wrapper(name):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2283 ''' Return a callable that relays calls to the current default app. '''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2284 @functools.wraps(getattr(Bottle, name))
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2285 def wrapper(*a, **ka):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2286 return getattr(app(), name)(*a, **ka)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2287 return wrapper
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2288
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2289 route = make_default_app_wrapper('route')
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2290 get = make_default_app_wrapper('get')
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2291 post = make_default_app_wrapper('post')
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2292 put = make_default_app_wrapper('put')
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2293 delete = make_default_app_wrapper('delete')
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2294 error = make_default_app_wrapper('error')
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2295 mount = make_default_app_wrapper('mount')
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2296 hook = make_default_app_wrapper('hook')
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2297 install = make_default_app_wrapper('install')
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2298 uninstall = make_default_app_wrapper('uninstall')
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2299 url = make_default_app_wrapper('get_url')
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2300
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2301
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2302
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2303
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2304
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2305
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2306
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2307 ###############################################################################
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2308 # Server Adapter ###############################################################
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2309 ###############################################################################
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2310
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2311
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2312 class ServerAdapter(object):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2313 quiet = False
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2314 def __init__(self, host='127.0.0.1', port=8080, **config):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2315 self.options = config
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2316 self.host = host
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2317 self.port = int(port)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2318
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2319 def run(self, handler): # pragma: no cover
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2320 pass
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2321
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2322 def __repr__(self):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2323 args = ', '.join(['%s=%s'%(k,repr(v)) for k, v in self.options.items()])
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2324 return "%s(%s)" % (self.__class__.__name__, args)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2325
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2326
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2327 class CGIServer(ServerAdapter):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2328 quiet = True
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2329 def run(self, handler): # pragma: no cover
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2330 from wsgiref.handlers import CGIHandler
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2331 def fixed_environ(environ, start_response):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2332 environ.setdefault('PATH_INFO', '')
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2333 return handler(environ, start_response)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2334 CGIHandler().run(fixed_environ)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2335
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2336
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2337 class FlupFCGIServer(ServerAdapter):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2338 def run(self, handler): # pragma: no cover
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2339 import flup.server.fcgi
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2340 self.options.setdefault('bindAddress', (self.host, self.port))
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2341 flup.server.fcgi.WSGIServer(handler, **self.options).run()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2342
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2343
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2344 class WSGIRefServer(ServerAdapter):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2345 def run(self, handler): # pragma: no cover
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2346 from wsgiref.simple_server import make_server, WSGIRequestHandler
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2347 if self.quiet:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2348 class QuietHandler(WSGIRequestHandler):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2349 def log_request(*args, **kw): pass
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2350 self.options['handler_class'] = QuietHandler
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2351 srv = make_server(self.host, self.port, handler, **self.options)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2352 srv.serve_forever()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2353
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2354
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2355 class CherryPyServer(ServerAdapter):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2356 def run(self, handler): # pragma: no cover
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2357 from cherrypy import wsgiserver
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2358 server = wsgiserver.CherryPyWSGIServer((self.host, self.port), handler)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2359 try:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2360 server.start()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2361 finally:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2362 server.stop()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2363
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2364
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2365 class WaitressServer(ServerAdapter):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2366 def run(self, handler):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2367 from waitress import serve
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2368 serve(handler, host=self.host, port=self.port)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2369
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2370
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2371 class PasteServer(ServerAdapter):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2372 def run(self, handler): # pragma: no cover
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2373 from paste import httpserver
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2374 if not self.quiet:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2375 from paste.translogger import TransLogger
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2376 handler = TransLogger(handler)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2377 httpserver.serve(handler, host=self.host, port=str(self.port),
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2378 **self.options)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2379
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2380
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2381 class MeinheldServer(ServerAdapter):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2382 def run(self, handler):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2383 from meinheld import server
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2384 server.listen((self.host, self.port))
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2385 server.run(handler)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2386
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2387
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2388 class FapwsServer(ServerAdapter):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2389 """ Extremely fast webserver using libev. See http://www.fapws.org/ """
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2390 def run(self, handler): # pragma: no cover
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2391 import fapws._evwsgi as evwsgi
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2392 from fapws import base, config
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2393 port = self.port
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2394 if float(config.SERVER_IDENT[-2:]) > 0.4:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2395 # fapws3 silently changed its API in 0.5
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2396 port = str(port)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2397 evwsgi.start(self.host, port)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2398 # fapws3 never releases the GIL. Complain upstream. I tried. No luck.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2399 if 'BOTTLE_CHILD' in os.environ and not self.quiet:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2400 _stderr("WARNING: Auto-reloading does not work with Fapws3.\n")
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2401 _stderr(" (Fapws3 breaks python thread support)\n")
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2402 evwsgi.set_base_module(base)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2403 def app(environ, start_response):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2404 environ['wsgi.multiprocess'] = False
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2405 return handler(environ, start_response)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2406 evwsgi.wsgi_cb(('', app))
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2407 evwsgi.run()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2408
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2409
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2410 class TornadoServer(ServerAdapter):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2411 """ The super hyped asynchronous server by facebook. Untested. """
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2412 def run(self, handler): # pragma: no cover
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2413 import tornado.wsgi, tornado.httpserver, tornado.ioloop
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2414 container = tornado.wsgi.WSGIContainer(handler)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2415 server = tornado.httpserver.HTTPServer(container)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2416 server.listen(port=self.port)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2417 tornado.ioloop.IOLoop.instance().start()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2418
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2419
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2420 class AppEngineServer(ServerAdapter):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2421 """ Adapter for Google App Engine. """
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2422 quiet = True
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2423 def run(self, handler):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2424 from google.appengine.ext.webapp import util
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2425 # A main() function in the handler script enables 'App Caching'.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2426 # Lets makes sure it is there. This _really_ improves performance.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2427 module = sys.modules.get('__main__')
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2428 if module and not hasattr(module, 'main'):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2429 module.main = lambda: util.run_wsgi_app(handler)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2430 util.run_wsgi_app(handler)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2431
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2432
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2433 class TwistedServer(ServerAdapter):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2434 """ Untested. """
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2435 def run(self, handler):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2436 from twisted.web import server, wsgi
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2437 from twisted.python.threadpool import ThreadPool
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2438 from twisted.internet import reactor
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2439 thread_pool = ThreadPool()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2440 thread_pool.start()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2441 reactor.addSystemEventTrigger('after', 'shutdown', thread_pool.stop)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2442 factory = server.Site(wsgi.WSGIResource(reactor, thread_pool, handler))
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2443 reactor.listenTCP(self.port, factory, interface=self.host)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2444 reactor.run()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2445
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2446
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2447 class DieselServer(ServerAdapter):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2448 """ Untested. """
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2449 def run(self, handler):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2450 from diesel.protocols.wsgi import WSGIApplication
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2451 app = WSGIApplication(handler, port=self.port)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2452 app.run()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2453
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2454
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2455 class GeventServer(ServerAdapter):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2456 """ Untested. Options:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2457
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2458 * `monkey` (default: True) fixes the stdlib to use greenthreads.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2459 * `fast` (default: False) uses libevent's http server, but has some
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2460 issues: No streaming, no pipelining, no SSL.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2461 """
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2462 def run(self, handler):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2463 from gevent import wsgi as wsgi_fast, pywsgi, monkey, local
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2464 if self.options.get('monkey', True):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2465 if not threading.local is local.local: monkey.patch_all()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2466 wsgi = wsgi_fast if self.options.get('fast') else pywsgi
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2467 log = None if self.quiet else 'default'
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2468 wsgi.WSGIServer((self.host, self.port), handler, log=log).serve_forever()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2469
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2470
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2471 class GunicornServer(ServerAdapter):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2472 """ Untested. See http://gunicorn.org/configure.html for options. """
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2473 def run(self, handler):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2474 from gunicorn.app.base import Application
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2475
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2476 config = {'bind': "%s:%d" % (self.host, int(self.port))}
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2477 config.update(self.options)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2478
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2479 class GunicornApplication(Application):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2480 def init(self, parser, opts, args):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2481 return config
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2482
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2483 def load(self):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2484 return handler
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2485
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2486 GunicornApplication().run()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2487
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2488
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2489 class EventletServer(ServerAdapter):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2490 """ Untested """
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2491 def run(self, handler):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2492 from eventlet import wsgi, listen
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2493 try:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2494 wsgi.server(listen((self.host, self.port)), handler,
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2495 log_output=(not self.quiet))
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2496 except TypeError:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2497 # Fallback, if we have old version of eventlet
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2498 wsgi.server(listen((self.host, self.port)), handler)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2499
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2500
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2501 class RocketServer(ServerAdapter):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2502 """ Untested. """
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2503 def run(self, handler):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2504 from rocket import Rocket
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2505 server = Rocket((self.host, self.port), 'wsgi', { 'wsgi_app' : handler })
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2506 server.start()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2507
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2508
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2509 class BjoernServer(ServerAdapter):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2510 """ Fast server written in C: https://github.com/jonashaag/bjoern """
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2511 def run(self, handler):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2512 from bjoern import run
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2513 run(handler, self.host, self.port)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2514
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2515
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2516 class AutoServer(ServerAdapter):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2517 """ Untested. """
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2518 adapters = [WaitressServer, PasteServer, TwistedServer, CherryPyServer, WSGIRefServer]
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2519 def run(self, handler):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2520 for sa in self.adapters:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2521 try:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2522 return sa(self.host, self.port, **self.options).run(handler)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2523 except ImportError:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2524 pass
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2525
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2526 server_names = {
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2527 'cgi': CGIServer,
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2528 'flup': FlupFCGIServer,
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2529 'wsgiref': WSGIRefServer,
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2530 'waitress': WaitressServer,
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2531 'cherrypy': CherryPyServer,
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2532 'paste': PasteServer,
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2533 'fapws3': FapwsServer,
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2534 'tornado': TornadoServer,
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2535 'gae': AppEngineServer,
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2536 'twisted': TwistedServer,
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2537 'diesel': DieselServer,
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2538 'meinheld': MeinheldServer,
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2539 'gunicorn': GunicornServer,
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2540 'eventlet': EventletServer,
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2541 'gevent': GeventServer,
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2542 'rocket': RocketServer,
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2543 'bjoern' : BjoernServer,
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2544 'auto': AutoServer,
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2545 }
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2546
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2547
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2548
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2549
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2550
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2551
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2552 ###############################################################################
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2553 # Application Control ##########################################################
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2554 ###############################################################################
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2555
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2556
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2557 def load(target, **namespace):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2558 """ Import a module or fetch an object from a module.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2559
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2560 * ``package.module`` returns `module` as a module object.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2561 * ``pack.mod:name`` returns the module variable `name` from `pack.mod`.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2562 * ``pack.mod:func()`` calls `pack.mod.func()` and returns the result.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2563
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2564 The last form accepts not only function calls, but any type of
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2565 expression. Keyword arguments passed to this function are available as
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2566 local variables. Example: ``import_string('re:compile(x)', x='[a-z]')``
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2567 """
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2568 module, target = target.split(":", 1) if ':' in target else (target, None)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2569 if module not in sys.modules: __import__(module)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2570 if not target: return sys.modules[module]
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2571 if target.isalnum(): return getattr(sys.modules[module], target)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2572 package_name = module.split('.')[0]
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2573 namespace[package_name] = sys.modules[package_name]
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2574 return eval('%s.%s' % (module, target), namespace)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2575
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2576
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2577 def load_app(target):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2578 """ Load a bottle application from a module and make sure that the import
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2579 does not affect the current default application, but returns a separate
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2580 application object. See :func:`load` for the target parameter. """
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2581 global NORUN; NORUN, nr_old = True, NORUN
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2582 try:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2583 tmp = default_app.push() # Create a new "default application"
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2584 rv = load(target) # Import the target module
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2585 return rv if callable(rv) else tmp
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2586 finally:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2587 default_app.remove(tmp) # Remove the temporary added default application
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2588 NORUN = nr_old
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2589
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2590 _debug = debug
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2591 def run(app=None, server='wsgiref', host='127.0.0.1', port=8080,
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2592 interval=1, reloader=False, quiet=False, plugins=None,
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2593 debug=False, **kargs):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2594 """ Start a server instance. This method blocks until the server terminates.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2595
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2596 :param app: WSGI application or target string supported by
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2597 :func:`load_app`. (default: :func:`default_app`)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2598 :param server: Server adapter to use. See :data:`server_names` keys
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2599 for valid names or pass a :class:`ServerAdapter` subclass.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2600 (default: `wsgiref`)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2601 :param host: Server address to bind to. Pass ``0.0.0.0`` to listens on
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2602 all interfaces including the external one. (default: 127.0.0.1)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2603 :param port: Server port to bind to. Values below 1024 require root
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2604 privileges. (default: 8080)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2605 :param reloader: Start auto-reloading server? (default: False)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2606 :param interval: Auto-reloader interval in seconds (default: 1)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2607 :param quiet: Suppress output to stdout and stderr? (default: False)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2608 :param options: Options passed to the server adapter.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2609 """
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2610 if NORUN: return
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2611 if reloader and not os.environ.get('BOTTLE_CHILD'):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2612 try:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2613 lockfile = None
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2614 fd, lockfile = tempfile.mkstemp(prefix='bottle.', suffix='.lock')
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2615 os.close(fd) # We only need this file to exist. We never write to it
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2616 while os.path.exists(lockfile):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2617 args = [sys.executable] + sys.argv
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2618 environ = os.environ.copy()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2619 environ['BOTTLE_CHILD'] = 'true'
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2620 environ['BOTTLE_LOCKFILE'] = lockfile
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2621 p = subprocess.Popen(args, env=environ)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2622 while p.poll() is None: # Busy wait...
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2623 os.utime(lockfile, None) # I am alive!
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2624 time.sleep(interval)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2625 if p.poll() != 3:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2626 if os.path.exists(lockfile): os.unlink(lockfile)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2627 sys.exit(p.poll())
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2628 except KeyboardInterrupt:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2629 pass
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2630 finally:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2631 if os.path.exists(lockfile):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2632 os.unlink(lockfile)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2633 return
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2634
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2635 try:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2636 _debug(debug)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2637 app = app or default_app()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2638 if isinstance(app, basestring):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2639 app = load_app(app)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2640 if not callable(app):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2641 raise ValueError("Application is not callable: %r" % app)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2642
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2643 for plugin in plugins or []:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2644 app.install(plugin)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2645
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2646 if server in server_names:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2647 server = server_names.get(server)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2648 if isinstance(server, basestring):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2649 server = load(server)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2650 if isinstance(server, type):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2651 server = server(host=host, port=port, **kargs)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2652 if not isinstance(server, ServerAdapter):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2653 raise ValueError("Unknown or unsupported server: %r" % server)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2654
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2655 server.quiet = server.quiet or quiet
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2656 if not server.quiet:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2657 _stderr("Bottle v%s server starting up (using %s)...\n" % (__version__, repr(server)))
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2658 _stderr("Listening on http://%s:%d/\n" % (server.host, server.port))
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2659 _stderr("Hit Ctrl-C to quit.\n\n")
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2660
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2661 if reloader:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2662 lockfile = os.environ.get('BOTTLE_LOCKFILE')
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2663 bgcheck = FileCheckerThread(lockfile, interval)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2664 with bgcheck:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2665 server.run(app)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2666 if bgcheck.status == 'reload':
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2667 sys.exit(3)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2668 else:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2669 server.run(app)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2670 except KeyboardInterrupt:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2671 pass
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2672 except (SystemExit, MemoryError):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2673 raise
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2674 except:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2675 if not reloader: raise
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2676 if not getattr(server, 'quiet', quiet):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2677 print_exc()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2678 time.sleep(interval)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2679 sys.exit(3)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2680
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2681
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2682
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2683 class FileCheckerThread(threading.Thread):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2684 ''' Interrupt main-thread as soon as a changed module file is detected,
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2685 the lockfile gets deleted or gets to old. '''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2686
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2687 def __init__(self, lockfile, interval):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2688 threading.Thread.__init__(self)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2689 self.lockfile, self.interval = lockfile, interval
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2690 #: Is one of 'reload', 'error' or 'exit'
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2691 self.status = None
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2692
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2693 def run(self):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2694 exists = os.path.exists
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2695 mtime = lambda path: os.stat(path).st_mtime
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2696 files = dict()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2697
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2698 for module in list(sys.modules.values()):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2699 path = getattr(module, '__file__', '')
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2700 if path[-4:] in ('.pyo', '.pyc'): path = path[:-1]
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2701 if path and exists(path): files[path] = mtime(path)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2702
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2703 while not self.status:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2704 if not exists(self.lockfile)\
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2705 or mtime(self.lockfile) < time.time() - self.interval - 5:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2706 self.status = 'error'
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2707 thread.interrupt_main()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2708 for path, lmtime in list(files.items()):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2709 if not exists(path) or mtime(path) > lmtime:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2710 self.status = 'reload'
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2711 thread.interrupt_main()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2712 break
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2713 time.sleep(self.interval)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2714
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2715 def __enter__(self):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2716 self.start()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2717
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2718 def __exit__(self, exc_type, exc_val, exc_tb):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2719 if not self.status: self.status = 'exit' # silent exit
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2720 self.join()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2721 return exc_type is not None and issubclass(exc_type, KeyboardInterrupt)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2722
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2723
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2724
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2725
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2726
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2727 ###############################################################################
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2728 # Template Adapters ############################################################
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2729 ###############################################################################
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2730
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2731
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2732 class TemplateError(HTTPError):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2733 def __init__(self, message):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2734 HTTPError.__init__(self, 500, message)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2735
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2736
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2737 class BaseTemplate(object):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2738 """ Base class and minimal API for template adapters """
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2739 extensions = ['tpl','html','thtml','stpl']
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2740 settings = {} #used in prepare()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2741 defaults = {} #used in render()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2742
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2743 def __init__(self, source=None, name=None, lookup=[], encoding='utf8', **settings):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2744 """ Create a new template.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2745 If the source parameter (str or buffer) is missing, the name argument
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2746 is used to guess a template filename. Subclasses can assume that
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2747 self.source and/or self.filename are set. Both are strings.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2748 The lookup, encoding and settings parameters are stored as instance
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2749 variables.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2750 The lookup parameter stores a list containing directory paths.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2751 The encoding parameter should be used to decode byte strings or files.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2752 The settings parameter contains a dict for engine-specific settings.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2753 """
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2754 self.name = name
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2755 self.source = source.read() if hasattr(source, 'read') else source
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2756 self.filename = source.filename if hasattr(source, 'filename') else None
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2757 self.lookup = [os.path.abspath(x) for x in lookup]
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2758 self.encoding = encoding
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2759 self.settings = self.settings.copy() # Copy from class variable
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2760 self.settings.update(settings) # Apply
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2761 if not self.source and self.name:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2762 self.filename = self.search(self.name, self.lookup)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2763 if not self.filename:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2764 raise TemplateError('Template %s not found.' % repr(name))
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2765 if not self.source and not self.filename:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2766 raise TemplateError('No template specified.')
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2767 self.prepare(**self.settings)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2768
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2769 @classmethod
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2770 def search(cls, name, lookup=[]):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2771 """ Search name in all directories specified in lookup.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2772 First without, then with common extensions. Return first hit. """
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2773 if os.path.isfile(name): return name
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2774 for spath in lookup:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2775 fname = os.path.join(spath, name)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2776 if os.path.isfile(fname):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2777 return fname
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2778 for ext in cls.extensions:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2779 if os.path.isfile('%s.%s' % (fname, ext)):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2780 return '%s.%s' % (fname, ext)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2781
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2782 @classmethod
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2783 def global_config(cls, key, *args):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2784 ''' This reads or sets the global settings stored in class.settings. '''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2785 if args:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2786 cls.settings = cls.settings.copy() # Make settings local to class
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2787 cls.settings[key] = args[0]
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2788 else:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2789 return cls.settings[key]
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2790
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2791 def prepare(self, **options):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2792 """ Run preparations (parsing, caching, ...).
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2793 It should be possible to call this again to refresh a template or to
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2794 update settings.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2795 """
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2796 raise NotImplementedError
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2797
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2798 def render(self, *args, **kwargs):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2799 """ Render the template with the specified local variables and return
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2800 a single byte or unicode string. If it is a byte string, the encoding
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2801 must match self.encoding. This method must be thread-safe!
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2802 Local variables may be provided in dictionaries (*args)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2803 or directly, as keywords (**kwargs).
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2804 """
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2805 raise NotImplementedError
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2806
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2807
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2808 class MakoTemplate(BaseTemplate):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2809 def prepare(self, **options):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2810 from mako.template import Template
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2811 from mako.lookup import TemplateLookup
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2812 options.update({'input_encoding':self.encoding})
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2813 options.setdefault('format_exceptions', bool(DEBUG))
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2814 lookup = TemplateLookup(directories=self.lookup, **options)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2815 if self.source:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2816 self.tpl = Template(self.source, lookup=lookup, **options)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2817 else:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2818 self.tpl = Template(uri=self.name, filename=self.filename, lookup=lookup, **options)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2819
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2820 def render(self, *args, **kwargs):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2821 for dictarg in args: kwargs.update(dictarg)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2822 _defaults = self.defaults.copy()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2823 _defaults.update(kwargs)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2824 return self.tpl.render(**_defaults)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2825
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2826
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2827 class CheetahTemplate(BaseTemplate):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2828 def prepare(self, **options):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2829 from Cheetah.Template import Template
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2830 self.context = threading.local()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2831 self.context.vars = {}
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2832 options['searchList'] = [self.context.vars]
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2833 if self.source:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2834 self.tpl = Template(source=self.source, **options)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2835 else:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2836 self.tpl = Template(file=self.filename, **options)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2837
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2838 def render(self, *args, **kwargs):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2839 for dictarg in args: kwargs.update(dictarg)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2840 self.context.vars.update(self.defaults)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2841 self.context.vars.update(kwargs)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2842 out = str(self.tpl)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2843 self.context.vars.clear()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2844 return out
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2845
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2846
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2847 class Jinja2Template(BaseTemplate):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2848 def prepare(self, filters=None, tests=None, **kwargs):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2849 from jinja2 import Environment, FunctionLoader
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2850 if 'prefix' in kwargs: # TODO: to be removed after a while
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2851 raise RuntimeError('The keyword argument `prefix` has been removed. '
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2852 'Use the full jinja2 environment name line_statement_prefix instead.')
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2853 self.env = Environment(loader=FunctionLoader(self.loader), **kwargs)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2854 if filters: self.env.filters.update(filters)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2855 if tests: self.env.tests.update(tests)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2856 if self.source:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2857 self.tpl = self.env.from_string(self.source)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2858 else:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2859 self.tpl = self.env.get_template(self.filename)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2860
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2861 def render(self, *args, **kwargs):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2862 for dictarg in args: kwargs.update(dictarg)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2863 _defaults = self.defaults.copy()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2864 _defaults.update(kwargs)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2865 return self.tpl.render(**_defaults)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2866
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2867 def loader(self, name):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2868 fname = self.search(name, self.lookup)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2869 if not fname: return
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2870 with open(fname, "rb") as f:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2871 return f.read().decode(self.encoding)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2872
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2873
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2874 class SimpleTALTemplate(BaseTemplate):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2875 ''' Deprecated, do not use. '''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2876 def prepare(self, **options):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2877 depr('The SimpleTAL template handler is deprecated'\
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2878 ' and will be removed in 0.12')
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2879 from simpletal import simpleTAL
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2880 if self.source:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2881 self.tpl = simpleTAL.compileHTMLTemplate(self.source)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2882 else:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2883 with open(self.filename, 'rb') as fp:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2884 self.tpl = simpleTAL.compileHTMLTemplate(tonat(fp.read()))
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2885
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2886 def render(self, *args, **kwargs):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2887 from simpletal import simpleTALES
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2888 for dictarg in args: kwargs.update(dictarg)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2889 context = simpleTALES.Context()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2890 for k,v in self.defaults.items():
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2891 context.addGlobal(k, v)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2892 for k,v in kwargs.items():
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2893 context.addGlobal(k, v)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2894 output = StringIO()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2895 self.tpl.expand(context, output)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2896 return output.getvalue()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2897
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2898
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2899 class SimpleTemplate(BaseTemplate):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2900 blocks = ('if', 'elif', 'else', 'try', 'except', 'finally', 'for', 'while',
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2901 'with', 'def', 'class')
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2902 dedent_blocks = ('elif', 'else', 'except', 'finally')
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2903
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2904 @lazy_attribute
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2905 def re_pytokens(cls):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2906 ''' This matches comments and all kinds of quoted strings but does
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2907 NOT match comments (#...) within quoted strings. (trust me) '''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2908 return re.compile(r'''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2909 (''(?!')|""(?!")|'{6}|"{6} # Empty strings (all 4 types)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2910 |'(?:[^\\']|\\.)+?' # Single quotes (')
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2911 |"(?:[^\\"]|\\.)+?" # Double quotes (")
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2912 |'{3}(?:[^\\]|\\.|\n)+?'{3} # Triple-quoted strings (')
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2913 |"{3}(?:[^\\]|\\.|\n)+?"{3} # Triple-quoted strings (")
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2914 |\#.* # Comments
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2915 )''', re.VERBOSE)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2916
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2917 def prepare(self, escape_func=html_escape, noescape=False, **kwargs):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2918 self.cache = {}
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2919 enc = self.encoding
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2920 self._str = lambda x: touni(x, enc)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2921 self._escape = lambda x: escape_func(touni(x, enc))
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2922 if noescape:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2923 self._str, self._escape = self._escape, self._str
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2924
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2925 @classmethod
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2926 def split_comment(cls, code):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2927 """ Removes comments (#...) from python code. """
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2928 if '#' not in code: return code
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2929 #: Remove comments only (leave quoted strings as they are)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2930 subf = lambda m: '' if m.group(0)[0]=='#' else m.group(0)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2931 return re.sub(cls.re_pytokens, subf, code)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2932
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2933 @cached_property
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2934 def co(self):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2935 return compile(self.code, self.filename or '<string>', 'exec')
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2936
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2937 @cached_property
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2938 def code(self):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2939 stack = [] # Current Code indentation
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2940 lineno = 0 # Current line of code
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2941 ptrbuffer = [] # Buffer for printable strings and token tuple instances
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2942 codebuffer = [] # Buffer for generated python code
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2943 multiline = dedent = oneline = False
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2944 template = self.source or open(self.filename, 'rb').read()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2945
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2946 def yield_tokens(line):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2947 for i, part in enumerate(re.split(r'\{\{(.*?)\}\}', line)):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2948 if i % 2:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2949 if part.startswith('!'): yield 'RAW', part[1:]
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2950 else: yield 'CMD', part
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2951 else: yield 'TXT', part
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2952
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2953 def flush(): # Flush the ptrbuffer
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2954 if not ptrbuffer: return
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2955 cline = ''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2956 for line in ptrbuffer:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2957 for token, value in line:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2958 if token == 'TXT': cline += repr(value)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2959 elif token == 'RAW': cline += '_str(%s)' % value
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2960 elif token == 'CMD': cline += '_escape(%s)' % value
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2961 cline += ', '
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2962 cline = cline[:-2] + '\\\n'
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2963 cline = cline[:-2]
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2964 if cline[:-1].endswith('\\\\\\\\\\n'):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2965 cline = cline[:-7] + cline[-1] # 'nobr\\\\\n' --> 'nobr'
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2966 cline = '_printlist([' + cline + '])'
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2967 del ptrbuffer[:] # Do this before calling code() again
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2968 code(cline)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2969
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2970 def code(stmt):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2971 for line in stmt.splitlines():
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2972 codebuffer.append(' ' * len(stack) + line.strip())
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2973
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2974 for line in template.splitlines(True):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2975 lineno += 1
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2976 line = touni(line, self.encoding)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2977 sline = line.lstrip()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2978 if lineno <= 2:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2979 m = re.match(r"%\s*#.*coding[:=]\s*([-\w.]+)", sline)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2980 if m: self.encoding = m.group(1)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2981 if m: line = line.replace('coding','coding (removed)')
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2982 if sline and sline[0] == '%' and sline[:2] != '%%':
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2983 line = line.split('%',1)[1].lstrip() # Full line following the %
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2984 cline = self.split_comment(line).strip()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2985 cmd = re.split(r'[^a-zA-Z0-9_]', cline)[0]
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2986 flush() # You are actually reading this? Good luck, it's a mess :)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2987 if cmd in self.blocks or multiline:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2988 cmd = multiline or cmd
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2989 dedent = cmd in self.dedent_blocks # "else:"
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2990 if dedent and not oneline and not multiline:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2991 cmd = stack.pop()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2992 code(line)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2993 oneline = not cline.endswith(':') # "if 1: pass"
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2994 multiline = cmd if cline.endswith('\\') else False
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2995 if not oneline and not multiline:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2996 stack.append(cmd)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2997 elif cmd == 'end' and stack:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2998 code('#end(%s) %s' % (stack.pop(), line.strip()[3:]))
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
2999 elif cmd == 'include':
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3000 p = cline.split(None, 2)[1:]
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3001 if len(p) == 2:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3002 code("_=_include(%s, _stdout, %s)" % (repr(p[0]), p[1]))
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3003 elif p:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3004 code("_=_include(%s, _stdout)" % repr(p[0]))
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3005 else: # Empty %include -> reverse of %rebase
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3006 code("_printlist(_base)")
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3007 elif cmd == 'rebase':
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3008 p = cline.split(None, 2)[1:]
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3009 if len(p) == 2:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3010 code("globals()['_rebase']=(%s, dict(%s))" % (repr(p[0]), p[1]))
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3011 elif p:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3012 code("globals()['_rebase']=(%s, {})" % repr(p[0]))
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3013 else:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3014 code(line)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3015 else: # Line starting with text (not '%') or '%%' (escaped)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3016 if line.strip().startswith('%%'):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3017 line = line.replace('%%', '%', 1)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3018 ptrbuffer.append(yield_tokens(line))
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3019 flush()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3020 return '\n'.join(codebuffer) + '\n'
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3021
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3022 def subtemplate(self, _name, _stdout, *args, **kwargs):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3023 for dictarg in args: kwargs.update(dictarg)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3024 if _name not in self.cache:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3025 self.cache[_name] = self.__class__(name=_name, lookup=self.lookup)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3026 return self.cache[_name].execute(_stdout, kwargs)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3027
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3028 def execute(self, _stdout, *args, **kwargs):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3029 for dictarg in args: kwargs.update(dictarg)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3030 env = self.defaults.copy()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3031 env.update({'_stdout': _stdout, '_printlist': _stdout.extend,
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3032 '_include': self.subtemplate, '_str': self._str,
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3033 '_escape': self._escape, 'get': env.get,
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3034 'setdefault': env.setdefault, 'defined': env.__contains__})
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3035 env.update(kwargs)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3036 eval(self.co, env)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3037 if '_rebase' in env:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3038 subtpl, rargs = env['_rebase']
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3039 rargs['_base'] = _stdout[:] #copy stdout
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3040 del _stdout[:] # clear stdout
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3041 return self.subtemplate(subtpl,_stdout,rargs)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3042 return env
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3043
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3044 def render(self, *args, **kwargs):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3045 """ Render the template using keyword arguments as local variables. """
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3046 for dictarg in args: kwargs.update(dictarg)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3047 stdout = []
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3048 self.execute(stdout, kwargs)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3049 return ''.join(stdout)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3050
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3051
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3052 def template(*args, **kwargs):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3053 '''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3054 Get a rendered template as a string iterator.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3055 You can use a name, a filename or a template string as first parameter.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3056 Template rendering arguments can be passed as dictionaries
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3057 or directly (as keyword arguments).
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3058 '''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3059 tpl = args[0] if args else None
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3060 template_adapter = kwargs.pop('template_adapter', SimpleTemplate)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3061 if tpl not in TEMPLATES or DEBUG:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3062 settings = kwargs.pop('template_settings', {})
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3063 lookup = kwargs.pop('template_lookup', TEMPLATE_PATH)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3064 if isinstance(tpl, template_adapter):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3065 TEMPLATES[tpl] = tpl
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3066 if settings: TEMPLATES[tpl].prepare(**settings)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3067 elif "\n" in tpl or "{" in tpl or "%" in tpl or '$' in tpl:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3068 TEMPLATES[tpl] = template_adapter(source=tpl, lookup=lookup, **settings)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3069 else:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3070 TEMPLATES[tpl] = template_adapter(name=tpl, lookup=lookup, **settings)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3071 if not TEMPLATES[tpl]:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3072 abort(500, 'Template (%s) not found' % tpl)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3073 for dictarg in args[1:]: kwargs.update(dictarg)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3074 return TEMPLATES[tpl].render(kwargs)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3075
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3076 mako_template = functools.partial(template, template_adapter=MakoTemplate)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3077 cheetah_template = functools.partial(template, template_adapter=CheetahTemplate)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3078 jinja2_template = functools.partial(template, template_adapter=Jinja2Template)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3079 simpletal_template = functools.partial(template, template_adapter=SimpleTALTemplate)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3080
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3081
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3082 def view(tpl_name, **defaults):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3083 ''' Decorator: renders a template for a handler.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3084 The handler can control its behavior like that:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3085
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3086 - return a dict of template vars to fill out the template
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3087 - return something other than a dict and the view decorator will not
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3088 process the template, but return the handler result as is.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3089 This includes returning a HTTPResponse(dict) to get,
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3090 for instance, JSON with autojson or other castfilters.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3091 '''
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3092 def decorator(func):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3093 @functools.wraps(func)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3094 def wrapper(*args, **kwargs):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3095 result = func(*args, **kwargs)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3096 if isinstance(result, (dict, DictMixin)):
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3097 tplvars = defaults.copy()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3098 tplvars.update(result)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3099 return template(tpl_name, **tplvars)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3100 return result
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3101 return wrapper
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3102 return decorator
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3103
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3104 mako_view = functools.partial(view, template_adapter=MakoTemplate)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3105 cheetah_view = functools.partial(view, template_adapter=CheetahTemplate)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3106 jinja2_view = functools.partial(view, template_adapter=Jinja2Template)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3107 simpletal_view = functools.partial(view, template_adapter=SimpleTALTemplate)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3108
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3109
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3110
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3111
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3112
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3113
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3114 ###############################################################################
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3115 # Constants and Globals ########################################################
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3116 ###############################################################################
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3117
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3118
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3119 TEMPLATE_PATH = ['./', './views/']
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3120 TEMPLATES = {}
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3121 DEBUG = False
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3122 NORUN = False # If set, run() does nothing. Used by load_app()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3123
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3124 #: A dict to map HTTP status codes (e.g. 404) to phrases (e.g. 'Not Found')
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3125 HTTP_CODES = httplib.responses
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3126 HTTP_CODES[418] = "I'm a teapot" # RFC 2324
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3127 HTTP_CODES[428] = "Precondition Required"
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3128 HTTP_CODES[429] = "Too Many Requests"
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3129 HTTP_CODES[431] = "Request Header Fields Too Large"
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3130 HTTP_CODES[511] = "Network Authentication Required"
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3131 _HTTP_STATUS_LINES = dict((k, '%d %s'%(k,v)) for (k,v) in HTTP_CODES.items())
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3132
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3133 #: The default template used for error pages. Override with @error()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3134 ERROR_PAGE_TEMPLATE = """
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3135 %%try:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3136 %%from %s import DEBUG, HTTP_CODES, request, touni
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3137 %%status_name = HTTP_CODES.get(e.status, 'Unknown').title()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3138 <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3139 <html>
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3140 <head>
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3141 <title>Error {{e.status}}: {{status_name}}</title>
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3142 <style type="text/css">
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3143 html {background-color: #eee; font-family: sans;}
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3144 body {background-color: #fff; border: 1px solid #ddd;
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3145 padding: 15px; margin: 15px;}
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3146 pre {background-color: #eee; border: 1px solid #ddd; padding: 5px;}
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3147 </style>
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3148 </head>
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3149 <body>
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3150 <h1>Error {{e.status}}: {{status_name}}</h1>
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3151 <p>Sorry, the requested URL <tt>{{repr(request.url)}}</tt>
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3152 caused an error:</p>
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3153 <pre>{{e.output}}</pre>
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3154 %%if DEBUG and e.exception:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3155 <h2>Exception:</h2>
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3156 <pre>{{repr(e.exception)}}</pre>
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3157 %%end
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3158 %%if DEBUG and e.traceback:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3159 <h2>Traceback:</h2>
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3160 <pre>{{e.traceback}}</pre>
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3161 %%end
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3162 </body>
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3163 </html>
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3164 %%except ImportError:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3165 <b>ImportError:</b> Could not generate the error page. Please add bottle to
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3166 the import path.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3167 %%end
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3168 """ % __name__
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3169
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3170 #: A thread-safe instance of :class:`LocalRequest`. If accessed from within a
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3171 #: request callback, this instance always refers to the *current* request
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3172 #: (even on a multithreaded server).
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3173 request = LocalRequest()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3174
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3175 #: A thread-safe instance of :class:`LocalResponse`. It is used to change the
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3176 #: HTTP response for the *current* request.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3177 response = LocalResponse()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3178
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3179 #: A thread-safe namespace. Not used by Bottle.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3180 local = threading.local()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3181
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3182 # Initialize app stack (create first empty Bottle app)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3183 # BC: 0.6.4 and needed for run()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3184 app = default_app = AppStack()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3185 app.push()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3186
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3187 #: A virtual package that redirects import statements.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3188 #: Example: ``import bottle.ext.sqlite`` actually imports `bottle_sqlite`.
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3189 ext = _ImportRedirect(__name__+'.ext', 'bottle_%s').module
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3190
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3191 if __name__ == '__main__':
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3192 opt, args, parser = _cmd_options, _cmd_args, _cmd_parser
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3193 if opt.version:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3194 _stdout('Bottle %s\n'%__version__)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3195 sys.exit(0)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3196 if not args:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3197 parser.print_help()
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3198 _stderr('\nError: No application specified.\n')
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3199 sys.exit(1)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3200
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3201 sys.path.insert(0, '.')
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3202 sys.modules.setdefault('bottle', sys.modules['__main__'])
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3203
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3204 host, port = (opt.bind or 'localhost'), 8080
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3205 if ':' in host:
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3206 host, port = host.rsplit(':', 1)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3207
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3208 run(args[0], host=host, port=port, server=opt.server,
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3209 reloader=opt.reload, plugins=opt.plugin, debug=opt.debug)
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3210
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3211
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3212
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3213
298e502fdcd4 Add some web server handling
Matt Johnston <matt@ucc.asn.au>
parents:
diff changeset
3214 # THE END