mirror of
https://github.com/python/cpython.git
synced 2025-09-26 18:29:57 +00:00
Closing patch #101120 -- After everyone agreed.
This commit is contained in:
parent
dc3d606bd9
commit
a1a4b5916b
3 changed files with 76 additions and 61 deletions
|
@ -45,7 +45,7 @@ telling the client what kind of data is following. Python code to
|
||||||
generate a minimal header section looks like this:
|
generate a minimal header section looks like this:
|
||||||
|
|
||||||
\begin{verbatim}
|
\begin{verbatim}
|
||||||
print "Content-type: text/html" # HTML is following
|
print "Content-Type: text/html" # HTML is following
|
||||||
print # blank line, end of headers
|
print # blank line, end of headers
|
||||||
\end{verbatim}
|
\end{verbatim}
|
||||||
|
|
||||||
|
@ -59,9 +59,6 @@ print "<H1>This is my first CGI script</H1>"
|
||||||
print "Hello, world!"
|
print "Hello, world!"
|
||||||
\end{verbatim}
|
\end{verbatim}
|
||||||
|
|
||||||
(It may not be fully legal HTML according to the letter of the
|
|
||||||
standard, but any browser will understand it.)
|
|
||||||
|
|
||||||
\subsection{Using the cgi module}
|
\subsection{Using the cgi module}
|
||||||
\nodename{Using the cgi module}
|
\nodename{Using the cgi module}
|
||||||
|
|
||||||
|
@ -77,9 +74,16 @@ value of various environment variables set according to the CGI
|
||||||
standard). Since it may consume standard input, it should be
|
standard). Since it may consume standard input, it should be
|
||||||
instantiated only once.
|
instantiated only once.
|
||||||
|
|
||||||
The \class{FieldStorage} instance can be accessed as if it were a Python
|
The \class{FieldStorage} instance can be indexed like a Python
|
||||||
dictionary. For instance, the following code (which assumes that the
|
dictionary, and also supports the standard dictionary methods
|
||||||
\code{content-type} header and blank line have already been printed)
|
\function{has_key()} and \function{keys()}.
|
||||||
|
Form fields containing empty strings are ignored
|
||||||
|
and do not appear in the dictionary; to keep such values, provide
|
||||||
|
the optional \samp{keep_blank_values} argument when creating the
|
||||||
|
\class {FieldStorage} instance.
|
||||||
|
|
||||||
|
For instance, the following code (which assumes that the
|
||||||
|
\code{Content-Type} header and blank line have already been printed)
|
||||||
checks that the fields \code{name} and \code{addr} are both set to a
|
checks that the fields \code{name} and \code{addr} are both set to a
|
||||||
non-empty string:
|
non-empty string:
|
||||||
|
|
||||||
|
@ -87,23 +91,30 @@ non-empty string:
|
||||||
form = cgi.FieldStorage()
|
form = cgi.FieldStorage()
|
||||||
form_ok = 0
|
form_ok = 0
|
||||||
if form.has_key("name") and form.has_key("addr"):
|
if form.has_key("name") and form.has_key("addr"):
|
||||||
if form["name"].value != "" and form["addr"].value != "":
|
form_ok = 1
|
||||||
form_ok = 1
|
|
||||||
if not form_ok:
|
if not form_ok:
|
||||||
print "<H1>Error</H1>"
|
print "<H1>Error</H1>"
|
||||||
print "Please fill in the name and addr fields."
|
print "Please fill in the name and addr fields."
|
||||||
return
|
return
|
||||||
|
print "<p>name:", form["name"].value
|
||||||
|
print "<p>addr:", form["addr"].value
|
||||||
...further form processing here...
|
...further form processing here...
|
||||||
\end{verbatim}
|
\end{verbatim}
|
||||||
|
|
||||||
Here the fields, accessed through \samp{form[\var{key}]}, are
|
Here the fields, accessed through \samp{form[\var{key}]}, are
|
||||||
themselves instances of \class{FieldStorage} (or
|
themselves instances of \class{FieldStorage} (or
|
||||||
\class{MiniFieldStorage}, depending on the form encoding).
|
\class{MiniFieldStorage}, depending on the form encoding).
|
||||||
|
The \member{value} attribute of the instance yields the string value
|
||||||
|
of the field. The \function{getvalue()} method returns this string value
|
||||||
|
directly; it also accepts an optional second argument as a default to
|
||||||
|
return if the requested key is not present.
|
||||||
|
|
||||||
If the submitted form data contains more than one field with the same
|
If the submitted form data contains more than one field with the same
|
||||||
name, the object retrieved by \samp{form[\var{key}]} is not a
|
name, the object retrieved by \samp{form[\var{key}]} is not a
|
||||||
\class{FieldStorage} or \class{MiniFieldStorage}
|
\class{FieldStorage} or \class{MiniFieldStorage}
|
||||||
instance but a list of such instances. If you expect this possibility
|
instance but a list of such instances. Similarly, in this situation,
|
||||||
|
\samp{form.getvalue(\var{key})} would return a list of strings.
|
||||||
|
If you expect this possibility
|
||||||
(i.e., when your HTML form contains multiple fields with the same
|
(i.e., when your HTML form contains multiple fields with the same
|
||||||
name), use the \function{type()} function to determine whether you
|
name), use the \function{type()} function to determine whether you
|
||||||
have a single instance or a list of instances. For example, here's
|
have a single instance or a list of instances. For example, here's
|
||||||
|
@ -111,27 +122,21 @@ code that concatenates any number of username fields, separated by
|
||||||
commas:
|
commas:
|
||||||
|
|
||||||
\begin{verbatim}
|
\begin{verbatim}
|
||||||
username = form["username"]
|
value = form.getvalue("username", "")
|
||||||
if type(username) is type([]):
|
if type(value) is type([]):
|
||||||
# Multiple username fields specified
|
# Multiple username fields specified
|
||||||
usernames = ""
|
usernames = ",".join(value)
|
||||||
for item in username:
|
|
||||||
if usernames:
|
|
||||||
# Next item -- insert comma
|
|
||||||
usernames = usernames + "," + item.value
|
|
||||||
else:
|
|
||||||
# First item -- don't insert comma
|
|
||||||
usernames = item.value
|
|
||||||
else:
|
else:
|
||||||
# Single username field specified
|
# Single or no username field specified
|
||||||
usernames = username.value
|
usernames = value
|
||||||
\end{verbatim}
|
\end{verbatim}
|
||||||
|
|
||||||
If a field represents an uploaded file, the value attribute reads the
|
If a field represents an uploaded file, accessing the value via the
|
||||||
|
\member{value} attribute or the \function{getvalue()} method reads the
|
||||||
entire file in memory as a string. This may not be what you want.
|
entire file in memory as a string. This may not be what you want.
|
||||||
You can test for an uploaded file by testing either the filename
|
You can test for an uploaded file by testing either the \member{filename}
|
||||||
attribute or the file attribute. You can then read the data at
|
attribute or the \member{file} attribute. You can then read the data at
|
||||||
leisure from the file attribute:
|
leisure from the \member{file} attribute:
|
||||||
|
|
||||||
\begin{verbatim}
|
\begin{verbatim}
|
||||||
fileitem = form["userfile"]
|
fileitem = form["userfile"]
|
||||||
|
@ -157,7 +162,8 @@ When a form is submitted in the ``old'' format (as the query string or
|
||||||
as a single data part of type
|
as a single data part of type
|
||||||
\mimetype{application/x-www-form-urlencoded}), the items will actually
|
\mimetype{application/x-www-form-urlencoded}), the items will actually
|
||||||
be instances of the class \class{MiniFieldStorage}. In this case, the
|
be instances of the class \class{MiniFieldStorage}. In this case, the
|
||||||
list, file and filename attributes are always \code{None}.
|
\member{list}, \member{file}, and \member{filename} attributes are
|
||||||
|
always \code{None}.
|
||||||
|
|
||||||
|
|
||||||
\subsection{Old classes}
|
\subsection{Old classes}
|
||||||
|
@ -233,23 +239,22 @@ exception.
|
||||||
\begin{funcdesc}{parse_multipart}{fp, pdict}
|
\begin{funcdesc}{parse_multipart}{fp, pdict}
|
||||||
Parse input of type \mimetype{multipart/form-data} (for
|
Parse input of type \mimetype{multipart/form-data} (for
|
||||||
file uploads). Arguments are \var{fp} for the input file and
|
file uploads). Arguments are \var{fp} for the input file and
|
||||||
\var{pdict} for the dictionary containing other parameters of
|
\var{pdict} for a dictionary containing other parameters in
|
||||||
\code{content-type} header
|
the \code{Content-Type} header.
|
||||||
|
|
||||||
Returns a dictionary just like \function{parse_qs()} keys are the
|
Returns a dictionary just like \function{parse_qs()} keys are the
|
||||||
field names, each value is a list of values for that field. This is
|
field names, each value is a list of values for that field. This is
|
||||||
easy to use but not much good if you are expecting megabytes to be
|
easy to use but not much good if you are expecting megabytes to be
|
||||||
uploaded --- in that case, use the \class{FieldStorage} class instead
|
uploaded --- in that case, use the \class{FieldStorage} class instead
|
||||||
which is much more flexible. Note that \code{content-type} is the
|
which is much more flexible.
|
||||||
raw, unparsed contents of the \code{content-type} header.
|
|
||||||
|
|
||||||
Note that this does not parse nested multipart parts --- use
|
Note that this does not parse nested multipart parts --- use
|
||||||
\class{FieldStorage} for that.
|
\class{FieldStorage} for that.
|
||||||
\end{funcdesc}
|
\end{funcdesc}
|
||||||
|
|
||||||
\begin{funcdesc}{parse_header}{string}
|
\begin{funcdesc}{parse_header}{string}
|
||||||
Parse a header like \code{content-type} into a main
|
Parse a MIME header (such as \code{Content-Type}) into a main
|
||||||
content-type and a dictionary of parameters.
|
value and a dictionary of parameters.
|
||||||
\end{funcdesc}
|
\end{funcdesc}
|
||||||
|
|
||||||
\begin{funcdesc}{test}{}
|
\begin{funcdesc}{test}{}
|
||||||
|
@ -432,7 +437,7 @@ For example:
|
||||||
\begin{verbatim}
|
\begin{verbatim}
|
||||||
import sys
|
import sys
|
||||||
import traceback
|
import traceback
|
||||||
print "Content-type: text/html"
|
print "Content-Type: text/html"
|
||||||
print
|
print
|
||||||
sys.stderr = sys.stdout
|
sys.stderr = sys.stdout
|
||||||
try:
|
try:
|
||||||
|
@ -454,7 +459,7 @@ built-in modules):
|
||||||
\begin{verbatim}
|
\begin{verbatim}
|
||||||
import sys
|
import sys
|
||||||
sys.stderr = sys.stdout
|
sys.stderr = sys.stdout
|
||||||
print "Content-type: text/plain"
|
print "Content-Type: text/plain"
|
||||||
print
|
print
|
||||||
...your code here...
|
...your code here...
|
||||||
\end{verbatim}
|
\end{verbatim}
|
||||||
|
|
46
Lib/cgi.py
46
Lib/cgi.py
|
@ -19,7 +19,7 @@ written in Python.
|
||||||
# responsible for its maintenance.
|
# responsible for its maintenance.
|
||||||
#
|
#
|
||||||
|
|
||||||
__version__ = "2.2"
|
__version__ = "2.3"
|
||||||
|
|
||||||
|
|
||||||
# Imports
|
# Imports
|
||||||
|
@ -31,6 +31,7 @@ import os
|
||||||
import urllib
|
import urllib
|
||||||
import mimetools
|
import mimetools
|
||||||
import rfc822
|
import rfc822
|
||||||
|
import UserDict
|
||||||
from StringIO import StringIO
|
from StringIO import StringIO
|
||||||
|
|
||||||
|
|
||||||
|
@ -166,11 +167,10 @@ def parse_qs(qs, keep_blank_values=0, strict_parsing=0):
|
||||||
"""
|
"""
|
||||||
dict = {}
|
dict = {}
|
||||||
for name, value in parse_qsl(qs, keep_blank_values, strict_parsing):
|
for name, value in parse_qsl(qs, keep_blank_values, strict_parsing):
|
||||||
if len(value) or keep_blank_values:
|
if dict.has_key(name):
|
||||||
if dict.has_key(name):
|
dict[name].append(value)
|
||||||
dict[name].append(value)
|
else:
|
||||||
else:
|
dict[name] = [value]
|
||||||
dict[name] = [value]
|
|
||||||
return dict
|
return dict
|
||||||
|
|
||||||
def parse_qsl(qs, keep_blank_values=0, strict_parsing=0):
|
def parse_qsl(qs, keep_blank_values=0, strict_parsing=0):
|
||||||
|
@ -201,9 +201,10 @@ def parse_qsl(qs, keep_blank_values=0, strict_parsing=0):
|
||||||
if strict_parsing:
|
if strict_parsing:
|
||||||
raise ValueError, "bad query field: %s" % `name_value`
|
raise ValueError, "bad query field: %s" % `name_value`
|
||||||
continue
|
continue
|
||||||
name = urllib.unquote(string.replace(nv[0], '+', ' '))
|
if len(nv[1]) or keep_blank_values:
|
||||||
value = urllib.unquote(string.replace(nv[1], '+', ' '))
|
name = urllib.unquote(string.replace(nv[0], '+', ' '))
|
||||||
r.append((name, value))
|
value = urllib.unquote(string.replace(nv[1], '+', ' '))
|
||||||
|
r.append((name, value))
|
||||||
|
|
||||||
return r
|
return r
|
||||||
|
|
||||||
|
@ -537,6 +538,17 @@ class FieldStorage:
|
||||||
else:
|
else:
|
||||||
return found
|
return found
|
||||||
|
|
||||||
|
def getvalue(self, key, default=None):
|
||||||
|
"""Dictionary style get() method, including 'value' lookup."""
|
||||||
|
if self.has_key(key):
|
||||||
|
value = self[key]
|
||||||
|
if type(value) is type([]):
|
||||||
|
return map(lambda v: v.value, value)
|
||||||
|
else:
|
||||||
|
return value.value
|
||||||
|
else:
|
||||||
|
return default
|
||||||
|
|
||||||
def keys(self):
|
def keys(self):
|
||||||
"""Dictionary style keys() method."""
|
"""Dictionary style keys() method."""
|
||||||
if self.list is None:
|
if self.list is None:
|
||||||
|
@ -706,7 +718,7 @@ class FieldStorage:
|
||||||
# Backwards Compatibility Classes
|
# Backwards Compatibility Classes
|
||||||
# ===============================
|
# ===============================
|
||||||
|
|
||||||
class FormContentDict:
|
class FormContentDict(UserDict.UserDict):
|
||||||
"""Basic (multiple values per field) form content as dictionary.
|
"""Basic (multiple values per field) form content as dictionary.
|
||||||
|
|
||||||
form = FormContentDict()
|
form = FormContentDict()
|
||||||
|
@ -720,20 +732,8 @@ class FormContentDict:
|
||||||
|
|
||||||
"""
|
"""
|
||||||
def __init__(self, environ=os.environ):
|
def __init__(self, environ=os.environ):
|
||||||
self.dict = parse(environ=environ)
|
self.dict = self.data = parse(environ=environ)
|
||||||
self.query_string = environ['QUERY_STRING']
|
self.query_string = environ['QUERY_STRING']
|
||||||
def __getitem__(self,key):
|
|
||||||
return self.dict[key]
|
|
||||||
def keys(self):
|
|
||||||
return self.dict.keys()
|
|
||||||
def has_key(self, key):
|
|
||||||
return self.dict.has_key(key)
|
|
||||||
def values(self):
|
|
||||||
return self.dict.values()
|
|
||||||
def items(self):
|
|
||||||
return self.dict.items()
|
|
||||||
def __len__( self ):
|
|
||||||
return len(self.dict)
|
|
||||||
|
|
||||||
|
|
||||||
class SvFormContentDict(FormContentDict):
|
class SvFormContentDict(FormContentDict):
|
||||||
|
|
|
@ -116,19 +116,27 @@ def main():
|
||||||
d = do_test(orig, "POST")
|
d = do_test(orig, "POST")
|
||||||
assert d == expect, "Error parsing %s" % repr(orig)
|
assert d == expect, "Error parsing %s" % repr(orig)
|
||||||
|
|
||||||
d = {'QUERY_STRING': orig}
|
env = {'QUERY_STRING': orig}
|
||||||
fcd = cgi.FormContentDict(d)
|
fcd = cgi.FormContentDict(env)
|
||||||
sd = cgi.SvFormContentDict(d)
|
sd = cgi.SvFormContentDict(env)
|
||||||
|
fs = cgi.FieldStorage(environ=env)
|
||||||
if type(expect) == type({}):
|
if type(expect) == type({}):
|
||||||
# test dict interface
|
# test dict interface
|
||||||
assert len(expect) == len(fcd)
|
assert len(expect) == len(fcd)
|
||||||
assert norm(expect.keys()) == norm(fcd.keys())
|
assert norm(expect.keys()) == norm(fcd.keys())
|
||||||
assert norm(expect.values()) == norm(fcd.values())
|
assert norm(expect.values()) == norm(fcd.values())
|
||||||
assert norm(expect.items()) == norm(fcd.items())
|
assert norm(expect.items()) == norm(fcd.items())
|
||||||
|
assert fcd.get("nonexistent field", "default") == "default"
|
||||||
|
assert len(sd) == len(fs)
|
||||||
|
assert norm(sd.keys()) == norm(fs.keys())
|
||||||
|
assert fs.getvalue("nonexistent field", "default") == "default"
|
||||||
|
# test individual fields
|
||||||
for key in expect.keys():
|
for key in expect.keys():
|
||||||
expect_val = expect[key]
|
expect_val = expect[key]
|
||||||
assert fcd.has_key(key)
|
assert fcd.has_key(key)
|
||||||
assert norm(fcd[key]) == norm(expect[key])
|
assert norm(fcd[key]) == norm(expect[key])
|
||||||
|
assert fcd.get(key, "default") == fcd[key]
|
||||||
|
assert fs.has_key(key)
|
||||||
if len(expect_val) > 1:
|
if len(expect_val) > 1:
|
||||||
single_value = 0
|
single_value = 0
|
||||||
else:
|
else:
|
||||||
|
@ -137,9 +145,11 @@ def main():
|
||||||
val = sd[key]
|
val = sd[key]
|
||||||
except IndexError:
|
except IndexError:
|
||||||
assert not single_value
|
assert not single_value
|
||||||
|
assert fs.getvalue(key) == expect_val
|
||||||
else:
|
else:
|
||||||
assert single_value
|
assert single_value
|
||||||
assert val == expect_val[0]
|
assert val == expect_val[0]
|
||||||
|
assert fs.getvalue(key) == expect_val[0]
|
||||||
assert norm(sd.getlist(key)) == norm(expect_val)
|
assert norm(sd.getlist(key)) == norm(expect_val)
|
||||||
if single_value:
|
if single_value:
|
||||||
assert norm(sd.values()) == \
|
assert norm(sd.values()) == \
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue