]> granicus.if.org Git - python/commitdiff
Closing patch #101120 -- After everyone agreed.
authorMoshe Zadka <moshez@math.huji.ac.il>
Fri, 25 Aug 2000 21:47:56 +0000 (21:47 +0000)
committerMoshe Zadka <moshez@math.huji.ac.il>
Fri, 25 Aug 2000 21:47:56 +0000 (21:47 +0000)
Doc/lib/libcgi.tex
Lib/cgi.py
Lib/test/test_cgi.py

index e90bf1cd8c9596d130a3e0aa054d18cc04042dae..285c08f66e4b3518da420a141b21fab364ace3b9 100644 (file)
@@ -45,7 +45,7 @@ telling the client what kind of data is following.  Python code to
 generate a minimal header section looks like this:
 
 \begin{verbatim}
-print "Content-type: text/html"     # HTML is following
+print "Content-Type: text/html"     # HTML is following
 print                               # blank line, end of headers
 \end{verbatim}
 
@@ -59,9 +59,6 @@ print "<H1>This is my first CGI script</H1>"
 print "Hello, world!"
 \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}
 \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
 instantiated only once.
 
-The \class{FieldStorage} instance can be accessed as if it were a Python 
-dictionary.  For instance, the following code (which assumes that the 
-\code{content-type} header and blank line have already been printed)
+The \class{FieldStorage} instance can be indexed like a Python
+dictionary, and also supports the standard dictionary methods
+\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
 non-empty string:
 
@@ -87,23 +91,30 @@ non-empty string:
 form = cgi.FieldStorage()
 form_ok = 0
 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:
     print "<H1>Error</H1>"
     print "Please fill in the name and addr fields."
     return
+print "<p>name:", form["name"].value
+print "<p>addr:", form["addr"].value
 ...further form processing here...
 \end{verbatim}
 
 Here the fields, accessed through \samp{form[\var{key}]}, are
 themselves instances of \class{FieldStorage} (or
 \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
 name, the object retrieved by \samp{form[\var{key}]} is not a
 \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
 name), use the \function{type()} function to determine whether you
 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:
 
 \begin{verbatim}
-username = form["username"]
-if type(username) is type([]):
+value = form.getvalue("username", "")
+if type(value) is type([]):
     # Multiple username fields specified
-    usernames = ""
-    for item in username:
-        if usernames:
-            # Next item -- insert comma
-            usernames = usernames + "," + item.value
-        else:
-            # First item -- don't insert comma
-            usernames = item.value
+    usernames = ",".join(value)
 else:
-    # Single username field specified
-    usernames = username.value
+    # Single or no username field specified
+    usernames = value
 \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.
-You can test for an uploaded file by testing either the filename
-attribute or the file attribute.  You can then read the data at
-leisure from the file attribute:
+You can test for an uploaded file by testing either the \member{filename}
+attribute or the \member{file} attribute.  You can then read the data at
+leisure from the \member{file} attribute:
 
 \begin{verbatim}
 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
 \mimetype{application/x-www-form-urlencoded}), the items will actually
 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}
@@ -233,23 +239,22 @@ exception.
 \begin{funcdesc}{parse_multipart}{fp, pdict}
 Parse input of type \mimetype{multipart/form-data} (for 
 file uploads).  Arguments are \var{fp} for the input file and
-\var{pdict} for the dictionary containing other parameters of
-\code{content-type} header
+\var{pdict} for a dictionary containing other parameters in
+the \code{Content-Type} header.
 
 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
 easy to use but not much good if you are expecting megabytes to be
 uploaded --- in that case, use the \class{FieldStorage} class instead
-which is much more flexible.  Note that \code{content-type} is the
-raw, unparsed contents of the \code{content-type} header.
+which is much more flexible.
 
 Note that this does not parse nested multipart parts --- use
 \class{FieldStorage} for that.
 \end{funcdesc}
 
 \begin{funcdesc}{parse_header}{string}
-Parse a header like \code{content-type} into a main
-content-type and a dictionary of parameters.
+Parse a MIME header (such as \code{Content-Type}) into a main
+value and a dictionary of parameters.
 \end{funcdesc}
 
 \begin{funcdesc}{test}{}
@@ -432,7 +437,7 @@ For example:
 \begin{verbatim}
 import sys
 import traceback
-print "Content-type: text/html"
+print "Content-Type: text/html"
 print
 sys.stderr = sys.stdout
 try:
@@ -454,7 +459,7 @@ built-in modules):
 \begin{verbatim}
 import sys
 sys.stderr = sys.stdout
-print "Content-type: text/plain"
+print "Content-Type: text/plain"
 print
 ...your code here...
 \end{verbatim}
index 0184472a531d505179f91b05651ab99c5eab5ca8..2a214050c703d476e0c72eefeb4c4fb386e4ca31 100755 (executable)
@@ -19,7 +19,7 @@ written in Python.
 # responsible for its maintenance.
 # 
 
-__version__ = "2.2"
+__version__ = "2.3"
 
 
 # Imports
@@ -31,6 +31,7 @@ import os
 import urllib
 import mimetools
 import rfc822
+import UserDict
 from StringIO import StringIO
 
 
@@ -166,11 +167,10 @@ def parse_qs(qs, keep_blank_values=0, strict_parsing=0):
     """
     dict = {}
     for name, value in parse_qsl(qs, keep_blank_values, strict_parsing):
-        if len(value) or keep_blank_values:
-            if dict.has_key(name):
-                dict[name].append(value)
-            else:
-                dict[name] = [value]
+        if dict.has_key(name):
+            dict[name].append(value)
+        else:
+            dict[name] = [value]
     return dict
 
 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:
                 raise ValueError, "bad query field: %s" % `name_value`
             continue
-        name = urllib.unquote(string.replace(nv[0], '+', ' '))
-        value = urllib.unquote(string.replace(nv[1], '+', ' '))
-        r.append((name, value))
+        if len(nv[1]) or keep_blank_values:
+            name = urllib.unquote(string.replace(nv[0], '+', ' '))
+            value = urllib.unquote(string.replace(nv[1], '+', ' '))
+            r.append((name, value))
 
     return r
 
@@ -537,6 +538,17 @@ class FieldStorage:
         else:
             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):
         """Dictionary style keys() method."""
         if self.list is None:
@@ -706,7 +718,7 @@ class FieldStorage:
 # Backwards Compatibility Classes
 # ===============================
 
-class FormContentDict:
+class FormContentDict(UserDict.UserDict):
     """Basic (multiple values per field) form content as dictionary.
 
     form = FormContentDict()
@@ -720,20 +732,8 @@ class FormContentDict:
 
     """
     def __init__(self, environ=os.environ):
-        self.dict = parse(environ=environ)
+        self.dict = self.data = parse(environ=environ)
         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):
index 61c3da78c82f79a9240d52c5166e656db16efb4c..29eb5a6c1850cd295524043f061c7501c9d5aa4f 100644 (file)
@@ -116,19 +116,27 @@ def main():
         d = do_test(orig, "POST")
         assert d == expect, "Error parsing %s" % repr(orig)
 
-        d = {'QUERY_STRING': orig}
-        fcd = cgi.FormContentDict(d)
-        sd = cgi.SvFormContentDict(d)
+        env = {'QUERY_STRING': orig}
+        fcd = cgi.FormContentDict(env)
+        sd = cgi.SvFormContentDict(env)
+        fs = cgi.FieldStorage(environ=env)
         if type(expect) == type({}):
             # test dict interface
             assert len(expect) == len(fcd)
             assert norm(expect.keys()) == norm(fcd.keys())
             assert norm(expect.values()) == norm(fcd.values())
             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():
                 expect_val = expect[key]
                 assert fcd.has_key(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:
                     single_value = 0
                 else:
@@ -137,9 +145,11 @@ def main():
                     val = sd[key]
                 except IndexError:
                     assert not single_value
+                    assert fs.getvalue(key) == expect_val
                 else:
                     assert single_value
                     assert val == expect_val[0]
+                    assert fs.getvalue(key) == expect_val[0]
                 assert norm(sd.getlist(key)) == norm(expect_val)
                 if single_value:
                     assert norm(sd.values()) == \