[Python-checkins] r64447 - in python/trunk: Doc/library/cgi.rst Lib/cgi.py Lib/test/test_cgi.py Misc/NEWS

facundo.batista python-checkins at python.org
Sat Jun 21 20:58:05 CEST 2008


Author: facundo.batista
Date: Sat Jun 21 20:58:04 2008
New Revision: 64447

Log:

Now a from submitted via POST that also has a query string
will contain both FieldStorage and MiniFieldStorage items.

Fixes #1817.


Modified:
   python/trunk/Doc/library/cgi.rst
   python/trunk/Lib/cgi.py
   python/trunk/Lib/test/test_cgi.py
   python/trunk/Misc/NEWS

Modified: python/trunk/Doc/library/cgi.rst
==============================================================================
--- python/trunk/Doc/library/cgi.rst	(original)
+++ python/trunk/Doc/library/cgi.rst	Sat Jun 21 20:58:04 2008
@@ -165,6 +165,8 @@
 actually be instances of the class :class:`MiniFieldStorage`.  In this case, the
 :attr:`list`, :attr:`file`, and :attr:`filename` attributes are always ``None``.
 
+A form submitted via POST that also has a query string will contain both
+:class:`FieldStorage` and :class:`MiniFieldStorage` items.
 
 Higher Level Interface
 ----------------------

Modified: python/trunk/Lib/cgi.py
==============================================================================
--- python/trunk/Lib/cgi.py	(original)
+++ python/trunk/Lib/cgi.py	Sat Jun 21 20:58:04 2008
@@ -456,6 +456,7 @@
         self.strict_parsing = strict_parsing
         if 'REQUEST_METHOD' in environ:
             method = environ['REQUEST_METHOD'].upper()
+        self.qs_on_post = None
         if method == 'GET' or method == 'HEAD':
             if 'QUERY_STRING' in environ:
                 qs = environ['QUERY_STRING']
@@ -474,6 +475,8 @@
                 headers['content-type'] = "application/x-www-form-urlencoded"
             if 'CONTENT_TYPE' in environ:
                 headers['content-type'] = environ['CONTENT_TYPE']
+            if 'QUERY_STRING' in environ:
+                self.qs_on_post = environ['QUERY_STRING']
             if 'CONTENT_LENGTH' in environ:
                 headers['content-length'] = environ['CONTENT_LENGTH']
         self.fp = fp or sys.stdin
@@ -631,6 +634,8 @@
     def read_urlencoded(self):
         """Internal: read data in query string format."""
         qs = self.fp.read(self.length)
+        if self.qs_on_post:
+            qs += '&' + self.qs_on_post
         self.list = list = []
         for key, value in parse_qsl(qs, self.keep_blank_values,
                                     self.strict_parsing):
@@ -645,6 +650,12 @@
         if not valid_boundary(ib):
             raise ValueError, 'Invalid boundary in multipart form: %r' % (ib,)
         self.list = []
+        if self.qs_on_post:
+            for key, value in parse_qsl(self.qs_on_post, self.keep_blank_values,
+                                        self.strict_parsing):
+                self.list.append(MiniFieldStorage(key, value))
+            FieldStorageClass = None
+
         klass = self.FieldStorageClass or self.__class__
         part = klass(self.fp, {}, ib,
                      environ, keep_blank_values, strict_parsing)

Modified: python/trunk/Lib/test/test_cgi.py
==============================================================================
--- python/trunk/Lib/test/test_cgi.py	(original)
+++ python/trunk/Lib/test/test_cgi.py	Sat Jun 21 20:58:04 2008
@@ -130,6 +130,17 @@
 def first_second_elts(list):
     return map(lambda p:(p[0], p[1][0]), list)
 
+def gen_result(data, environ):
+    fake_stdin = StringIO(data)
+    fake_stdin.seek(0)
+    form = cgi.FieldStorage(fp=fake_stdin, environ=environ)
+
+    result = {}
+    for k, v in dict(form).items():
+        result[k] = type(v) is list and form.getlist(k) or v.value
+
+    return result
+
 class CgiTests(unittest.TestCase):
 
     def test_qsl(self):
@@ -278,6 +289,83 @@
                 got = getattr(fs.list[x], k)
                 self.assertEquals(got, exp)
 
+    _qs_result = {
+        'key1': 'value1',
+        'key2': ['value2x', 'value2y'],
+        'key3': 'value3',
+        'key4': 'value4'
+    }
+    def testQSAndUrlEncode(self):
+        data = "key2=value2x&key3=value3&key4=value4"
+        environ = {
+            'CONTENT_LENGTH':   str(len(data)),
+            'CONTENT_TYPE':     'application/x-www-form-urlencoded',
+            'QUERY_STRING':     'key1=value1&key2=value2y',
+            'REQUEST_METHOD':   'POST',
+        }
+        v = gen_result(data, environ)
+        self.assertEqual(self._qs_result, v)
+
+    def testQSAndFormData(self):
+        data = """
+---123
+Content-Disposition: form-data; name="key2"
+
+value2y
+---123
+Content-Disposition: form-data; name="key3"
+
+value3
+---123
+Content-Disposition: form-data; name="key4"
+
+value4
+---123--
+"""
+        environ = {
+            'CONTENT_LENGTH':   str(len(data)),
+            'CONTENT_TYPE':     'multipart/form-data; boundary=-123',
+            'QUERY_STRING':     'key1=value1&key2=value2x',
+            'REQUEST_METHOD':   'POST',
+        }
+        v = gen_result(data, environ)
+        self.assertEqual(self._qs_result, v)
+
+    def testQSAndFormDataFile(self):
+        data = """
+---123
+Content-Disposition: form-data; name="key2"
+
+value2y
+---123
+Content-Disposition: form-data; name="key3"
+
+value3
+---123
+Content-Disposition: form-data; name="key4"
+
+value4
+---123
+Content-Disposition: form-data; name="upload"; filename="fake.txt"
+Content-Type: text/plain
+
+this is the content of the fake file
+
+---123--
+"""
+        environ = {
+            'CONTENT_LENGTH':   str(len(data)),
+            'CONTENT_TYPE':     'multipart/form-data; boundary=-123',
+            'QUERY_STRING':     'key1=value1&key2=value2x',
+            'REQUEST_METHOD':   'POST',
+        }
+        result = self._qs_result.copy()
+        result.update({
+            'upload': 'this is the content of the fake file\n'
+        })
+        v = gen_result(data, environ)
+        self.assertEqual(result, v)
+
 def test_main():
     run_unittest(CgiTests)
 

Modified: python/trunk/Misc/NEWS
==============================================================================
--- python/trunk/Misc/NEWS	(original)
+++ python/trunk/Misc/NEWS	Sat Jun 21 20:58:04 2008
@@ -112,6 +112,8 @@
   structures, to match the behaviour of 2.5 and 3.0 (now follows the common
   sense).
 
+- Issue #1817: cgi now correctly handles the querystring on POST requests
+
 - Issue #3136: fileConfig()'s disabling of old loggers is now conditional via
   an optional disable_existing_loggers parameter, but the default value is
   such that the old behaviour is preserved. Thanks to Leandro Lucarella for


More information about the Python-checkins mailing list