Removed some leftovers from media.xsl.
[enkel.git] / enkel / forminput.py
blobc06e7a9ec63bd2adc5e9b3603c7dce757a97ef70
1 # This file is part of the Enkel web programming library.
3 # Copyright (C) 2007 Espen Angell Kristiansen (espeak@users.sourceforge.net)
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License
7 # as published by the Free Software Foundation; either version 2
8 # of the License, or (at your option) any later version.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 """ Simplified form input parsing similare to cgi.FieldStorage
20 (uses enkel.wansgli.formparse) """
23 from enkel.wansgli.formparse import parse_query_string, parse_multipart,\
24 FormParams
25 import settings
29 class FormInputError(Exception):
30 pass
32 class FormInputMethodError(FormInputError):
33 pass
35 class FormInputClientError(FormInputError):
36 def __init__(self, msg):
37 msg = "Client bug/error: " + msg
38 FormInputError.__init__(self, msg)
42 class Any(object):
43 """ When you have data in more than one
44 L{enkel.wansgli.formparse.FormParams}, and do not care from
45 which FormParams the data is retrived, you can use Any.
46 It implements a subset of the functions provided by
47 FormParams. """
48 def __init__(self, *form_params):
49 """
50 @param form_params: tuple of L{enkel.wansgli.formparse.FormParams}
51 sorted in the preferred order.
52 """
53 self.form_params = form_params
55 def get(self, key, default=None):
56 """ Get the list of parameters from the first FormParams
57 containing "key", or return "default".
58 """
59 for p in self.form_params:
60 if key in p:
61 return p[key]
62 return default
64 def getfirst(self, key, default=None):
65 """ Get the first item in the list of parameters from the
66 first FormParams containing "key", or return "default".
67 """
68 v = self.get(key)
69 if v == None:
70 return default
71 else:
72 return v[0]
74 def __call__(self, key, default=None):
75 """ Alias for L{getfirst} """
76 return self.getfirst(key, default)
78 def __contains__(self, key):
79 """ Check if "key" is in any of the FormParams. """
80 for p in self.form_params:
81 if key in p:
82 return True
83 else:
84 return False
86 def __repr__(self):
87 b = ""
88 for p in self.form_params:
89 b += repr(p)
90 return b
93 class FormInput(object):
94 """
95 >>> from cStringIO import StringIO
96 >>> from enkel.wansgli.apptester import encode_multipart
99 Using GET method
100 ================
101 >>> env = dict(
102 ... QUERY_STRING = "name=john&id=8812&x=10&x=20",
103 ... REQUEST_METHOD = "GET"
104 ... )
105 >>> f = FormInput(env)
106 >>> f.GET
107 {u'x': [u'10', u'20'], u'name': [u'john'], u'id': [u'8812']}
108 >>> f.POST
110 >>> f.GET.get("x")
111 [u'10', u'20']
112 >>> f.GET("x")
113 u'10'
114 >>> f.GET("x") == f.GET.getfirst("x")
115 True
118 Using POST method
119 =================
120 >>> q = "a=10&a=20&name=peter"
121 >>> env = dict()
122 >>> env["REQUEST_METHOD"] = "POST"
123 >>> env["wsgi.input"] = StringIO(q)
124 >>> env["CONTENT_LENGTH"] = len(q)
125 >>> env["CONTENT_TYPE"] = "application/x-www-form-urlencoded"
127 >>> f = FormInput(env)
128 >>> f.POST
129 {u'a': [u'10', u'20'], u'name': [u'peter']}
130 >>> f.POST.get("name")
131 [u'peter']
132 >>> f.POST("a")
133 u'10'
136 any and default
137 ===============
138 >>> env = dict(
139 ... QUERY_STRING = "name=john&id=8812&x=10&x=20",
140 ... REQUEST_METHOD = "GET"
141 ... )
143 any is used if you do not care about the method.
145 >>> f = FormInput(env)
146 >>> f.any
147 {u'x': [u'10', u'20'], u'name': [u'john'], u'id': [u'8812']}{}
148 >>> f.any("name")
149 u'john'
150 >>> f.any.getfirst("x")
151 u'10'
153 defaults can be used just like with dict.get()
155 >>> f.any("does not exist", 1000)
156 1000
159 Testing multipart request
160 =========================
161 >>> params = [
162 ... ("myfile", "this is a test", "myfile.png", "image/png"),
163 ... ("x", "10"),
164 ... ("y", "20", "y1.txt"),
165 ... ("y", "30", "y2.txt")
166 ... ]
167 >>> boundary, q = encode_multipart(params)
169 >>> env = dict()
170 >>> env["REQUEST_METHOD"] = "POST"
171 >>> env["wsgi.input"] = StringIO(q)
172 >>> env["CONTENT_LENGTH"] = len(q)
173 >>> env["CONTENT_TYPE"] = "multipart/form-data; boundary=%s" % boundary
175 >>> f = FormInput(env)
177 >>> f.files.getfirst("myfile").read()
178 'this is a test'
179 >>> f.files.getfirst("myfile").filename
180 'myfile.png'
181 >>> f.files("myfile").content_type
182 'image/png'
185 Testing unicode support
186 =======================
187 Note that we specify encoding="utf-8", but this is not
188 really required as utf-8 is the default.
190 >>> env = {
191 ... "REQUEST_METHOD": "GET",
192 ... "QUERY_STRING": u"name=\u00e5ge".encode("utf-8")
193 ... }
194 >>> f = FormInput(env, encoding="utf-8")
195 >>> f.GET.getfirst("name") == u"\u00e5ge"
196 True
199 @ivar GET: L{enkel.wansgli.formparse.FormParams} object containing
200 all variables retrived from a GET request. All values
201 are unicode strings.
202 @ivar POST: L{enkel.wansgli.formparse.FormParams} object containing all
203 variables retrived from a POST request. All values are
204 unicode strings.
205 @ivar any: L{Any} object containing all variables
206 retrived from a POST or GET request. All values are strings.
207 @ivar files: L{enkel.wansgli.formparse.FormParams} object containing all
208 files retrived from a multipart POST request. All values are
209 L{enkel.wansgli.formparse.MultipartFile} objects.
212 def __init__(self, env, encoding=settings.encoding):
213 self.env = env
214 self.method = env["REQUEST_METHOD"]
215 self.encoding = encoding
216 self.GET = FormParams()
217 self.POST = FormParams()
218 self.files = FormParams()
220 if self.method == "POST":
221 self._parse_post()
222 elif self.method == "GET":
223 self._parse_get()
225 self.any = Any(self.GET, self.POST)
228 def _parse_get(self):
229 s = self.env.get("QUERY_STRING", "")
230 self.GET = parse_query_string(s, self.encoding)
232 def _parse_post(self):
234 # error check and gather required env variables
235 try:
236 length = long(self.env["CONTENT_LENGTH"])
237 except KeyError:
238 raise FormInputClientError(
239 "POST request without a CONTENT_LENGTH header.")
240 except ValueError:
241 raise FormInputClientError(
242 "CONTENT_LENGTH header could not be converted to int.")
243 try:
244 content_type = self.env["CONTENT_TYPE"]
245 except KeyError:
246 raise FormInputClientError(
247 "POST request without a CONTENT_TYPE header")
250 # check the content-type and parse body accordingly
251 if content_type == "application/x-www-form-urlencoded":
252 body = self.env["wsgi.input"].read(length)
253 self.POST = parse_query_string(body, self.encoding)
254 elif content_type.startswith("multipart/form-data"):
255 self.files, self.POST = parse_multipart(content_type,
256 length, self.env["wsgi.input"], self.encoding)
259 def get(self, name, default):
260 """ Alias for any.get(name, default). """
261 return self.any.get(name, default)
263 def getfirst(self, name, default):
264 """ Alias for any.getfirst(name, default). """
265 return self.any.getfirst(name, default)
267 def __call__(self, name, default=None):
268 """ Alias for getfirst(name, default). """
269 return self.getfirst(name, default)
273 def suite():
274 import doctest
275 return doctest.DocTestSuite()
277 if __name__ == "__main__":
278 from enkel.wansgli.testhelpers import run_suite
279 run_suite(suite())