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
,\
29 class FormInputError(Exception):
32 class FormInputMethodError(FormInputError
):
35 class FormInputClientError(FormInputError
):
36 def __init__(self
, msg
):
37 msg
= "Client bug/error: " + msg
38 FormInputError
.__init
__(self
, msg
)
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
48 def __init__(self
, *form_params
):
50 @param form_params: tuple of L{enkel.wansgli.formparse.FormParams}
51 sorted in the preferred order.
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".
59 for p
in self
.form_params
:
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".
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
:
88 for p
in self
.form_params
:
93 class FormInput(object):
95 >>> from cStringIO import StringIO
96 >>> from enkel.wansgli.apptester import encode_multipart
102 ... QUERY_STRING = "name=john&id=8812&x=10&x=20",
103 ... REQUEST_METHOD = "GET"
105 >>> f = FormInput(env)
107 {u'x': [u'10', u'20'], u'name': [u'john'], u'id': [u'8812']}
114 >>> f.GET("x") == f.GET.getfirst("x")
120 >>> q = "a=10&a=20&name=peter"
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)
129 {u'a': [u'10', u'20'], u'name': [u'peter']}
130 >>> f.POST.get("name")
139 ... QUERY_STRING = "name=john&id=8812&x=10&x=20",
140 ... REQUEST_METHOD = "GET"
143 any is used if you do not care about the method.
145 >>> f = FormInput(env)
147 {u'x': [u'10', u'20'], u'name': [u'john'], u'id': [u'8812']}{}
150 >>> f.any.getfirst("x")
153 defaults can be used just like with dict.get()
155 >>> f.any("does not exist", 1000)
159 Testing multipart request
160 =========================
162 ... ("myfile", "this is a test", "myfile.png", "image/png"),
164 ... ("y", "20", "y1.txt"),
165 ... ("y", "30", "y2.txt")
167 >>> boundary, q = encode_multipart(params)
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()
179 >>> f.files.getfirst("myfile").filename
181 >>> f.files("myfile").content_type
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.
191 ... "REQUEST_METHOD": "GET",
192 ... "QUERY_STRING": u"name=\u00e5ge".encode("utf-8")
194 >>> f = FormInput(env, encoding="utf-8")
195 >>> f.GET.getfirst("name") == u"\u00e5ge"
199 @ivar GET: L{enkel.wansgli.formparse.FormParams} object containing
200 all variables retrived from a GET request. All values
202 @ivar POST: L{enkel.wansgli.formparse.FormParams} object containing all
203 variables retrived from a POST request. All values are
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
):
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":
222 elif self
.method
== "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
236 length
= long(self
.env
["CONTENT_LENGTH"])
238 raise FormInputClientError(
239 "POST request without a CONTENT_LENGTH header.")
241 raise FormInputClientError(
242 "CONTENT_LENGTH header could not be converted to int.")
244 content_type
= self
.env
["CONTENT_TYPE"]
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
)
275 return doctest
.DocTestSuite()
277 if __name__
== "__main__":
278 from enkel
.wansgli
.testhelpers
import run_suite