Index: Doc/library/urllib.parse.rst =================================================================== --- Doc/library/urllib.parse.rst (revision 64589) +++ Doc/library/urllib.parse.rst (working copy) @@ -98,6 +98,46 @@ states that these are equivalent). +.. function:: parse_qs(qs[, keep_blank_values[, strict_parsing]]) + + Parse a query string given as a string argument (data of type + :mimetype:`application/x-www-form-urlencoded`). Data are returned as a + dictionary. The dictionary keys are the unique query variable names and the + values are lists of values for each name. + + The optional argument *keep_blank_values* is a flag indicating whether blank + values in URL encoded queries should be treated as blank strings. A true value + indicates that blanks should be retained as blank strings. The default false + value indicates that blank values are to be ignored and treated as if they were + not included. + + The optional argument *strict_parsing* is a flag indicating what to do with + parsing errors. If false (the default), errors are silently ignored. If true, + errors raise a :exc:`ValueError` exception. + + Use the :func:`urllib.parse.urlencode` function to convert such dictionaries into + query strings. + + +.. function:: parse_qsl(qs[, keep_blank_values[, strict_parsing]]) + + Parse a query string given as a string argument (data of type + :mimetype:`application/x-www-form-urlencoded`). Data are returned as a list of + name, value pairs. + + The optional argument *keep_blank_values* is a flag indicating whether blank + values in URL encoded queries should be treated as blank strings. A true value + indicates that blanks should be retained as blank strings. The default false + value indicates that blank values are to be ignored and treated as if they were + not included. + + The optional argument *strict_parsing* is a flag indicating what to do with + parsing errors. If false (the default), errors are silently ignored. If true, + errors raise a :exc:`ValueError` exception. + + Use the :func:`urllib.parse.urlencode` function to convert such lists of pairs into + query strings. + .. function:: urlsplit(urlstring[, default_scheme[, allow_fragments]]) This is similar to :func:`urlparse`, but does not split the params from the URL. Index: Doc/library/cgi.rst =================================================================== --- Doc/library/cgi.rst (revision 64589) +++ Doc/library/cgi.rst (working copy) @@ -255,57 +255,15 @@ Parse a query in the environment or from a file (the file defaults to ``sys.stdin``). The *keep_blank_values* and *strict_parsing* parameters are - passed to :func:`parse_qs` unchanged. + passed to :func:`urllib.parse.parse_qs` unchanged. - -.. function:: parse_qs(qs[, keep_blank_values[, strict_parsing]]) - - Parse a query string given as a string argument (data of type - :mimetype:`application/x-www-form-urlencoded`). Data are returned as a - dictionary. The dictionary keys are the unique query variable names and the - values are lists of values for each name. - - The optional argument *keep_blank_values* is a flag indicating whether blank - values in URL encoded queries should be treated as blank strings. A true value - indicates that blanks should be retained as blank strings. The default false - value indicates that blank values are to be ignored and treated as if they were - not included. - - The optional argument *strict_parsing* is a flag indicating what to do with - parsing errors. If false (the default), errors are silently ignored. If true, - errors raise a :exc:`ValueError` exception. - - Use the :func:`urllib.urlencode` function to convert such dictionaries into - query strings. - - -.. function:: parse_qsl(qs[, keep_blank_values[, strict_parsing]]) - - Parse a query string given as a string argument (data of type - :mimetype:`application/x-www-form-urlencoded`). Data are returned as a list of - name, value pairs. - - The optional argument *keep_blank_values* is a flag indicating whether blank - values in URL encoded queries should be treated as blank strings. A true value - indicates that blanks should be retained as blank strings. The default false - value indicates that blank values are to be ignored and treated as if they were - not included. - - The optional argument *strict_parsing* is a flag indicating what to do with - parsing errors. If false (the default), errors are silently ignored. If true, - errors raise a :exc:`ValueError` exception. - - Use the :func:`urllib.urlencode` function to convert such lists of pairs into - query strings. - - .. function:: parse_multipart(fp, pdict) Parse input of type :mimetype:`multipart/form-data` (for file uploads). Arguments are *fp* for the input file and *pdict* for a dictionary containing other parameters in the :mailheader:`Content-Type` header. - Returns a dictionary just like :func:`parse_qs` keys are the field names, each + Returns a dictionary just like :func:`urllib.parse.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. Index: Lib/urllib/parse.py =================================================================== --- Lib/urllib/parse.py (revision 64589) +++ Lib/urllib/parse.py (working copy) @@ -5,7 +5,7 @@ """ __all__ = ["urlparse", "urlunparse", "urljoin", "urldefrag", - "urlsplit", "urlunsplit"] + "urlsplit", "urlunsplit","parse_qs","parse_qsl"] # A classification of schemes ('' means apply by default) uses_relative = ['ftp', 'http', 'gopher', 'nntp', 'imap', @@ -338,6 +338,74 @@ return s.replace(' ', '+') return quote(s, safe) +def parse_qs(qs, keep_blank_values=0, strict_parsing=0): + """Parse a query given as a string argument. + + Arguments: + + qs: URL-encoded query string to be parsed + + keep_blank_values: flag indicating whether blank values in + URL encoded queries should be treated as blank strings. + A true value indicates that blanks should be retained as + blank strings. The default false value indicates that + blank values are to be ignored and treated as if they were + not included. + + strict_parsing: flag indicating what to do with parsing errors. + If false (the default), errors are silently ignored. + If true, errors raise a ValueError exception. + """ + dict = {} + for name, value in parse_qsl(qs, keep_blank_values, strict_parsing): + if name in dict: + dict[name].append(value) + else: + dict[name] = [value] + return dict + +def parse_qsl(qs, keep_blank_values=0, strict_parsing=0): + """Parse a query given as a string argument. + + Arguments: + + qs: URL-encoded query string to be parsed + + keep_blank_values: flag indicating whether blank values in + URL encoded queries should be treated as blank strings. A + true value indicates that blanks should be retained as blank + strings. The default false value indicates that blank values + are to be ignored and treated as if they were not included. + + strict_parsing: flag indicating what to do with parsing errors. If + false (the default), errors are silently ignored. If true, + errors raise a ValueError exception. + + Returns a list, as G-d intended. + """ + pairs = [s2 for s1 in qs.split('&') for s2 in s1.split(';')] + r = [] + for name_value in pairs: + if not name_value and not strict_parsing: + continue + nv = name_value.split('=', 1) + if len(nv) != 2: + if strict_parsing: + raise ValueError("bad query field: %r" % (name_value,)) + # Handle case of a control-name with no equal sign + if keep_blank_values: + nv.append('') + else: + continue + if len(nv[1]) or keep_blank_values: + name = unquote(nv[0].replace('+', ' ')) + value = unquote(nv[1].replace('+', ' ')) + r.append((name, value)) + + return r + + + def urlencode(query,doseq=0): """Encode a sequence of two-element tuples or dictionary into a URL query string. Index: Lib/cgi.py =================================================================== --- Lib/cgi.py (revision 64589) +++ Lib/cgi.py (working copy) @@ -155,74 +155,6 @@ environ['QUERY_STRING'] = qs # XXX Shouldn't, really return parse_qs(qs, keep_blank_values, strict_parsing) - -def parse_qs(qs, keep_blank_values=0, strict_parsing=0): - """Parse a query given as a string argument. - - Arguments: - - qs: URL-encoded query string to be parsed - - keep_blank_values: flag indicating whether blank values in - URL encoded queries should be treated as blank strings. - A true value indicates that blanks should be retained as - blank strings. The default false value indicates that - blank values are to be ignored and treated as if they were - not included. - - strict_parsing: flag indicating what to do with parsing errors. - If false (the default), errors are silently ignored. - If true, errors raise a ValueError exception. - """ - dict = {} - for name, value in parse_qsl(qs, keep_blank_values, strict_parsing): - if name in dict: - dict[name].append(value) - else: - dict[name] = [value] - return dict - -def parse_qsl(qs, keep_blank_values=0, strict_parsing=0): - """Parse a query given as a string argument. - - Arguments: - - qs: URL-encoded query string to be parsed - - keep_blank_values: flag indicating whether blank values in - URL encoded queries should be treated as blank strings. A - true value indicates that blanks should be retained as blank - strings. The default false value indicates that blank values - are to be ignored and treated as if they were not included. - - strict_parsing: flag indicating what to do with parsing errors. If - false (the default), errors are silently ignored. If true, - errors raise a ValueError exception. - - Returns a list, as G-d intended. - """ - pairs = [s2 for s1 in qs.split('&') for s2 in s1.split(';')] - r = [] - for name_value in pairs: - if not name_value and not strict_parsing: - continue - nv = name_value.split('=', 1) - if len(nv) != 2: - if strict_parsing: - raise ValueError("bad query field: %r" % (name_value,)) - # Handle case of a control-name with no equal sign - if keep_blank_values: - nv.append('') - else: - continue - if len(nv[1]) or keep_blank_values: - name = urllib.parse.unquote(nv[0].replace('+', ' ')) - value = urllib.parse.unquote(nv[1].replace('+', ' ')) - r.append((name, value)) - - return r - - def parse_multipart(fp, pdict): """Parse multipart input. @@ -343,7 +275,11 @@ pdict[name] = value return key, pdict +# parse query string functions from urllib.parse +parse_qs = urllib.parse.parse_qs +parse_qsl = urllib.parse.parse_qsl + # Classes for field storage # ========================= Index: Lib/test/test_cgi.py =================================================================== --- Lib/test/test_cgi.py (revision 64589) +++ Lib/test/test_cgi.py (working copy) @@ -56,20 +56,6 @@ # A list of test cases. Each test case is a a two-tuple that contains # a string with the query and a dictionary with the expected result. -parse_qsl_test_cases = [ - ("", []), - ("&", []), - ("&&", []), - ("=", [('', '')]), - ("=a", [('', 'a')]), - ("a", [('a', '')]), - ("a=", [('a', '')]), - ("a=", [('a', '')]), - ("&a=b", [('a', 'b')]), - ("a=a+b&b=b+c", [('a', 'a b'), ('b', 'b c')]), - ("a=1&a=2", [('a', '1'), ('a', '2')]), -] - parse_strict_test_cases = [ ("", ValueError("bad query field: ''")), ("&", ValueError("bad query field: ''")), @@ -129,10 +115,6 @@ class CgiTests(unittest.TestCase): - def test_qsl(self): - for orig, expect in parse_qsl_test_cases: - result = cgi.parse_qsl(orig, keep_blank_values=True) - self.assertEqual(result, expect, "Error parsing %s" % repr(orig)) def test_strict(self): for orig, expect in parse_strict_test_cases: Index: Lib/test/test_urlparse.py =================================================================== --- Lib/test/test_urlparse.py (revision 64589) +++ Lib/test/test_urlparse.py (working copy) @@ -7,6 +7,24 @@ RFC1808_BASE = "http://a/b/c/d;p?q#f" RFC2396_BASE = "http://a/b/c/d;p?q" +# parse query string test cases. Each test case is a two-tuple that contains a +# string with the query and a dictionary with the expected results. + +parse_qsl_test_cases = [ + ("", []), + ("&", []), + ("&&", []), + ("=", [('', '')]), + ("=a", [('', 'a')]), + ("a", [('a', '')]), + ("a=", [('a', '')]), + ("a=", [('a', '')]), + ("&a=b", [('a', 'b')]), + ("a=a+b&b=b+c", [('a', 'a b'), ('b', 'b c')]), + ("a=1&a=2", [('a', '1'), ('a', '2')]), +] + + class UrlParseTestCase(unittest.TestCase): def checkRoundtrips(self, url, parsed, split): @@ -309,6 +327,10 @@ # Issue 1637: http://foo.com?query is legal self.assertEqual(urllib.parse.urlparse("http://example.com?blahblah=/foo"), ('http', 'example.com', '', '', 'blahblah=/foo', '')) + def test_qsl(self): + for orig, expect in parse_qsl_test_cases: + result = urllib.parse.parse_qsl(orig, keep_blank_values=True) + self.assertEqual(result, expect, "Error parsing %s" % repr(orig)) def test_main(): support.run_unittest(UrlParseTestCase)