diff options
Diffstat (limited to 'Lib/test/test_cgi.py')
-rw-r--r-- | Lib/test/test_cgi.py | 371 |
1 files changed, 125 insertions, 246 deletions
diff --git a/Lib/test/test_cgi.py b/Lib/test/test_cgi.py index ab86771..743c2af 100644 --- a/Lib/test/test_cgi.py +++ b/Lib/test/test_cgi.py @@ -1,11 +1,12 @@ +from io import BytesIO +from test.test_support import run_unittest, check_warnings import cgi import os import sys import tempfile import unittest + from collections import namedtuple -from io import StringIO, BytesIO -from test import support class HackedSysModule: # The regression test will have real values in sys.argv, which @@ -15,6 +16,11 @@ class HackedSysModule: cgi.sys = HackedSysModule() +try: + from cStringIO import StringIO +except ImportError: + from StringIO import StringIO + class ComparableException: def __init__(self, err): self.err = err @@ -22,11 +28,13 @@ class ComparableException: def __str__(self): return str(self.err) - def __eq__(self, anExc): + def __cmp__(self, anExc): if not isinstance(anExc, Exception): - return NotImplemented - return (self.err.__class__ == anExc.__class__ and - self.err.args == anExc.args) + return -1 + x = cmp(self.err.__class__, anExc.__class__) + if x != 0: + return x + return cmp(self.err.args, anExc.args) def __getattr__(self, attr): return getattr(self.err, attr) @@ -38,15 +46,15 @@ def do_test(buf, method): env['REQUEST_METHOD'] = 'GET' env['QUERY_STRING'] = buf elif method == "POST": - fp = BytesIO(buf.encode('latin-1')) # FieldStorage expects bytes + fp = StringIO(buf) env['REQUEST_METHOD'] = 'POST' env['CONTENT_TYPE'] = 'application/x-www-form-urlencoded' env['CONTENT_LENGTH'] = str(len(buf)) else: - raise ValueError("unknown method: %s" % method) + raise ValueError, "unknown method: %s" % method try: return cgi.parse(fp, env, strict_parsing=1) - except Exception as err: + except StandardError, err: return ComparableException(err) parse_strict_test_cases = [ @@ -96,20 +104,16 @@ parse_strict_test_cases = [ }) ] -def norm(seq): - return sorted(seq, key=repr) - def first_elts(list): - return [p[0] for p in list] + return map(lambda x:x[0], list) def first_second_elts(list): - return [(p[0], p[1][0]) for p in list] + return map(lambda p:(p[0], p[1][0]), list) def gen_result(data, environ): - encoding = 'latin-1' - fake_stdin = BytesIO(data.encode(encoding)) + fake_stdin = StringIO(data) fake_stdin.seek(0) - form = cgi.FieldStorage(fp=fake_stdin, environ=environ, encoding=encoding) + form = cgi.FieldStorage(fp=fake_stdin, environ=environ) result = {} for k, v in dict(form).items(): @@ -119,73 +123,70 @@ def gen_result(data, environ): class CgiTests(unittest.TestCase): - def test_parse_multipart(self): - fp = BytesIO(POSTDATA.encode('latin1')) - env = {'boundary': BOUNDARY.encode('latin1'), - 'CONTENT-LENGTH': '558'} - result = cgi.parse_multipart(fp, env) - expected = {'submit': [' Add '], 'id': ['1234'], - 'file': [b'Testing 123.\n'], 'title': ['']} - self.assertEqual(result, expected) - - def test_parse_multipart_invalid_encoding(self): - BOUNDARY = "JfISa01" - POSTDATA = """--JfISa01 -Content-Disposition: form-data; name="submit-name" -Content-Length: 3 - -\u2603 ---JfISa01""" - fp = BytesIO(POSTDATA.encode('utf8')) - env = {'boundary': BOUNDARY.encode('latin1'), - 'CONTENT-LENGTH': str(len(POSTDATA.encode('utf8')))} - result = cgi.parse_multipart(fp, env, encoding="ascii", - errors="surrogateescape") - expected = {'submit-name': ["\udce2\udc98\udc83"]} - self.assertEqual(result, expected) - self.assertEqual("\u2603".encode('utf8'), - result["submit-name"][0].encode('utf8', 'surrogateescape')) - - def test_fieldstorage_properties(self): - fs = cgi.FieldStorage() - self.assertFalse(fs) - self.assertIn("FieldStorage", repr(fs)) - self.assertEqual(list(fs), list(fs.keys())) - fs.list.append(namedtuple('MockFieldStorage', 'name')('fieldvalue')) - self.assertTrue(fs) - - def test_fieldstorage_invalid(self): - self.assertRaises(TypeError, cgi.FieldStorage, "not-a-file-obj", - environ={"REQUEST_METHOD":"PUT"}) - self.assertRaises(TypeError, cgi.FieldStorage, "foo", "bar") - fs = cgi.FieldStorage(headers={'content-type':'text/plain'}) - self.assertRaises(TypeError, bool, fs) + def test_escape(self): + self.assertEqual("test & string", cgi.escape("test & string")) + self.assertEqual("<test string>", cgi.escape("<test string>")) + self.assertEqual(""test string"", cgi.escape('"test string"', True)) def test_strict(self): for orig, expect in parse_strict_test_cases: # Test basic parsing d = do_test(orig, "GET") - self.assertEqual(d, expect, "Error parsing %s method GET" % repr(orig)) + self.assertEqual(d, expect, "Error parsing %s" % repr(orig)) d = do_test(orig, "POST") - self.assertEqual(d, expect, "Error parsing %s method POST" % repr(orig)) + self.assertEqual(d, expect, "Error parsing %s" % repr(orig)) env = {'QUERY_STRING': orig} + fcd = cgi.FormContentDict(env) + sd = cgi.SvFormContentDict(env) fs = cgi.FieldStorage(environ=env) if isinstance(expect, dict): # test dict interface - self.assertEqual(len(expect), len(fs)) - self.assertCountEqual(expect.keys(), fs.keys()) - ##self.assertEqual(norm(expect.values()), norm(fs.values())) - ##self.assertEqual(norm(expect.items()), norm(fs.items())) + self.assertEqual(len(expect), len(fcd)) + self.assertItemsEqual(expect.keys(), fcd.keys()) + self.assertItemsEqual(expect.values(), fcd.values()) + self.assertItemsEqual(expect.items(), fcd.items()) + self.assertEqual(fcd.get("nonexistent field", "default"), "default") + self.assertEqual(len(sd), len(fs)) + self.assertItemsEqual(sd.keys(), fs.keys()) self.assertEqual(fs.getvalue("nonexistent field", "default"), "default") # test individual fields for key in expect.keys(): expect_val = expect[key] - self.assertIn(key, fs) + self.assertTrue(fcd.has_key(key)) + self.assertItemsEqual(fcd[key], expect[key]) + self.assertEqual(fcd.get(key, "default"), fcd[key]) + self.assertTrue(fs.has_key(key)) if len(expect_val) > 1: + single_value = 0 + else: + single_value = 1 + try: + val = sd[key] + except IndexError: + self.assertFalse(single_value) self.assertEqual(fs.getvalue(key), expect_val) else: + self.assertTrue(single_value) + self.assertEqual(val, expect_val[0]) self.assertEqual(fs.getvalue(key), expect_val[0]) + self.assertItemsEqual(sd.getlist(key), expect_val) + if single_value: + self.assertItemsEqual(sd.values(), + first_elts(expect.values())) + self.assertItemsEqual(sd.items(), + first_second_elts(expect.items())) + + def test_weird_formcontentdict(self): + # Test the weird FormContentDict classes + env = {'QUERY_STRING': "x=1&y=2.0&z=2-3.%2b0&1=1abc"} + expect = {'x': 1, 'y': 2.0, 'z': '2-3.+0', '1': '1abc'} + d = cgi.InterpFormContentDict(env) + for k, v in expect.items(): + self.assertEqual(d[k], v) + for k, v in d.items(): + self.assertEqual(expect[k], v) + self.assertItemsEqual(expect.values(), d.values()) def test_log(self): cgi.log("Testing") @@ -194,11 +195,10 @@ Content-Length: 3 cgi.initlog("%s", "Testing initlog 1") cgi.log("%s", "Testing log 2") self.assertEqual(cgi.logfp.getvalue(), "Testing initlog 1\nTesting log 2\n") - if os.path.exists(os.devnull): + if os.path.exists("/dev/null"): cgi.logfp = None - cgi.logfile = os.devnull + cgi.logfile = "/dev/null" cgi.initlog("%s", "Testing log 3") - self.addCleanup(cgi.closelog) cgi.log("Testing log 4") def test_fieldstorage_readline(self): @@ -225,77 +225,65 @@ Content-Length: 3 setattr(self, name, a) return a - f = TestReadlineFile(tempfile.TemporaryFile("wb+")) - self.addCleanup(f.close) - f.write(b'x' * 256 * 1024) + f = TestReadlineFile(tempfile.TemporaryFile()) + f.write('x' * 256 * 1024) f.seek(0) env = {'REQUEST_METHOD':'PUT'} fs = cgi.FieldStorage(fp=f, environ=env) - self.addCleanup(fs.file.close) # if we're not chunking properly, readline is only called twice # (by read_binary); if we are chunking properly, it will be called 5 times # as long as the chunksize is 1 << 16. self.assertGreater(f.numcalls, 2) - f.close() + + def test_fieldstorage_invalid(self): + fs = cgi.FieldStorage() + self.assertFalse(fs) + self.assertRaises(TypeError, bool(fs)) + self.assertEqual(list(fs), list(fs.keys())) + fs.list.append(namedtuple('MockFieldStorage', 'name')('fieldvalue')) + self.assertTrue(fs) def test_fieldstorage_multipart(self): #Test basic FieldStorage multipart parsing - env = { - 'REQUEST_METHOD': 'POST', - 'CONTENT_TYPE': 'multipart/form-data; boundary={}'.format(BOUNDARY), - 'CONTENT_LENGTH': '558'} - fp = BytesIO(POSTDATA.encode('latin-1')) - fs = cgi.FieldStorage(fp, environ=env, encoding="latin-1") - self.assertEqual(len(fs.list), 4) - expect = [{'name':'id', 'filename':None, 'value':'1234'}, - {'name':'title', 'filename':None, 'value':''}, - {'name':'file', 'filename':'test.txt', 'value':b'Testing 123.\n'}, - {'name':'submit', 'filename':None, 'value':' Add '}] - for x in range(len(fs.list)): - for k, exp in expect[x].items(): - got = getattr(fs.list[x], k) - self.assertEqual(got, exp) + env = {'REQUEST_METHOD':'POST', 'CONTENT_TYPE':'multipart/form-data; boundary=---------------------------721837373350705526688164684', 'CONTENT_LENGTH':'558'} + postdata = """-----------------------------721837373350705526688164684 +Content-Disposition: form-data; name="id" - def test_fieldstorage_multipart_leading_whitespace(self): - env = { - 'REQUEST_METHOD': 'POST', - 'CONTENT_TYPE': 'multipart/form-data; boundary={}'.format(BOUNDARY), - 'CONTENT_LENGTH': '560'} - # Add some leading whitespace to our post data that will cause the - # first line to not be the innerboundary. - fp = BytesIO(b"\r\n" + POSTDATA.encode('latin-1')) - fs = cgi.FieldStorage(fp, environ=env, encoding="latin-1") +1234 +-----------------------------721837373350705526688164684 +Content-Disposition: form-data; name="title" + + +-----------------------------721837373350705526688164684 +Content-Disposition: form-data; name="file"; filename="test.txt" +Content-Type: text/plain + +Testing 123. + +-----------------------------721837373350705526688164684 +Content-Disposition: form-data; name="submit" + + Add\x20 +-----------------------------721837373350705526688164684-- +""" + fs = cgi.FieldStorage(fp=StringIO(postdata), environ=env) self.assertEqual(len(fs.list), 4) expect = [{'name':'id', 'filename':None, 'value':'1234'}, {'name':'title', 'filename':None, 'value':''}, - {'name':'file', 'filename':'test.txt', 'value':b'Testing 123.\n'}, + {'name':'file', 'filename':'test.txt','value':'Testing 123.\n'}, {'name':'submit', 'filename':None, 'value':' Add '}] for x in range(len(fs.list)): for k, exp in expect[x].items(): got = getattr(fs.list[x], k) self.assertEqual(got, exp) - def test_fieldstorage_multipart_non_ascii(self): - #Test basic FieldStorage multipart parsing - env = {'REQUEST_METHOD':'POST', - 'CONTENT_TYPE': 'multipart/form-data; boundary={}'.format(BOUNDARY), - 'CONTENT_LENGTH':'558'} - for encoding in ['iso-8859-1','utf-8']: - fp = BytesIO(POSTDATA_NON_ASCII.encode(encoding)) - fs = cgi.FieldStorage(fp, environ=env,encoding=encoding) - self.assertEqual(len(fs.list), 1) - expect = [{'name':'id', 'filename':None, 'value':'\xe7\xf1\x80'}] - for x in range(len(fs.list)): - for k, exp in expect[x].items(): - got = getattr(fs.list[x], k) - self.assertEqual(got, exp) - def test_fieldstorage_multipart_maxline(self): # Issue #18167 maxline = 1 << 16 self.maxDiff = None def check(content): - data = """---123 + data = """ +---123 Content-Disposition: form-data; name="upload"; filename="fake.txt" Content-Type: text/plain @@ -307,79 +295,11 @@ Content-Type: text/plain 'CONTENT_TYPE': 'multipart/form-data; boundary=-123', 'REQUEST_METHOD': 'POST', } - self.assertEqual(gen_result(data, environ), - {'upload': content.encode('latin1')}) + self.assertEqual(gen_result(data, environ), {'upload': content}) check('x' * (maxline - 1)) check('x' * (maxline - 1) + '\r') check('x' * (maxline - 1) + '\r' + 'y' * (maxline - 1)) - def test_fieldstorage_multipart_w3c(self): - # Test basic FieldStorage multipart parsing (W3C sample) - env = { - 'REQUEST_METHOD': 'POST', - 'CONTENT_TYPE': 'multipart/form-data; boundary={}'.format(BOUNDARY_W3), - 'CONTENT_LENGTH': str(len(POSTDATA_W3))} - fp = BytesIO(POSTDATA_W3.encode('latin-1')) - fs = cgi.FieldStorage(fp, environ=env, encoding="latin-1") - self.assertEqual(len(fs.list), 2) - self.assertEqual(fs.list[0].name, 'submit-name') - self.assertEqual(fs.list[0].value, 'Larry') - self.assertEqual(fs.list[1].name, 'files') - files = fs.list[1].value - self.assertEqual(len(files), 2) - expect = [{'name': None, 'filename': 'file1.txt', 'value': b'... contents of file1.txt ...'}, - {'name': None, 'filename': 'file2.gif', 'value': b'...contents of file2.gif...'}] - for x in range(len(files)): - for k, exp in expect[x].items(): - got = getattr(files[x], k) - self.assertEqual(got, exp) - - def test_fieldstorage_part_content_length(self): - BOUNDARY = "JfISa01" - POSTDATA = """--JfISa01 -Content-Disposition: form-data; name="submit-name" -Content-Length: 5 - -Larry ---JfISa01""" - env = { - 'REQUEST_METHOD': 'POST', - 'CONTENT_TYPE': 'multipart/form-data; boundary={}'.format(BOUNDARY), - 'CONTENT_LENGTH': str(len(POSTDATA))} - fp = BytesIO(POSTDATA.encode('latin-1')) - fs = cgi.FieldStorage(fp, environ=env, encoding="latin-1") - self.assertEqual(len(fs.list), 1) - self.assertEqual(fs.list[0].name, 'submit-name') - self.assertEqual(fs.list[0].value, 'Larry') - - def test_field_storage_multipart_no_content_length(self): - fp = BytesIO(b"""--MyBoundary -Content-Disposition: form-data; name="my-arg"; filename="foo" - -Test - ---MyBoundary-- -""") - env = { - "REQUEST_METHOD": "POST", - "CONTENT_TYPE": "multipart/form-data; boundary=MyBoundary", - "wsgi.input": fp, - } - fields = cgi.FieldStorage(fp, environ=env) - - self.assertEqual(len(fields["my-arg"].file.read()), 5) - - def test_fieldstorage_as_context_manager(self): - fp = BytesIO(b'x' * 10) - env = {'REQUEST_METHOD': 'PUT'} - with cgi.FieldStorage(fp=fp, environ=env) as fs: - content = fs.file.read() - self.assertFalse(fs.file.closed) - self.assertTrue(fs.file.closed) - self.assertEqual(content, 'x' * 10) - with self.assertRaisesRegex(ValueError, 'I/O operation on closed file'): - fs.file.read() - _qs_result = { 'key1': 'value1', 'key2': ['value2x', 'value2y'], @@ -452,7 +372,8 @@ a=5 ) def testQSAndFormData(self): - data = """---123 + data = """ +---123 Content-Disposition: form-data; name="key2" value2y @@ -476,7 +397,8 @@ value4 self.assertEqual(self._qs_result, v) def testQSAndFormDataFile(self): - data = """---123 + data = """ +---123 Content-Disposition: form-data; name="key2" value2y @@ -504,11 +426,25 @@ this is the content of the fake file } result = self._qs_result.copy() result.update({ - 'upload': b'this is the content of the fake file\n' + 'upload': 'this is the content of the fake file\n' }) v = gen_result(data, environ) self.assertEqual(result, v) + def test_deprecated_parse_qs(self): + # this func is moved to urlparse, this is just a sanity check + with check_warnings(('cgi.parse_qs is deprecated, use urlparse.' + 'parse_qs instead', PendingDeprecationWarning)): + self.assertEqual({'a': ['A1'], 'B': ['B3'], 'b': ['B2']}, + cgi.parse_qs('a=A1&b=B2&B=B3')) + + def test_deprecated_parse_qsl(self): + # this func is moved to urlparse, this is just a sanity check + with check_warnings(('cgi.parse_qsl is deprecated, use urlparse.' + 'parse_qsl instead', PendingDeprecationWarning)): + self.assertEqual([('a', 'A1'), ('b', 'B2'), ('B', 'B3')], + cgi.parse_qsl('a=A1&b=B2&B=B3')) + def test_parse_header(self): self.assertEqual( cgi.parse_header("text/plain"), @@ -538,66 +474,9 @@ this is the content of the fake file cgi.parse_header('form-data; name="files"; filename="fo\\"o;bar"'), ("form-data", {"name": "files", "filename": 'fo"o;bar'})) - def test_all(self): - blacklist = {"logfile", "logfp", "initlog", "dolog", "nolog", - "closelog", "log", "maxlen", "valid_boundary"} - support.check__all__(self, cgi, blacklist=blacklist) - - -BOUNDARY = "---------------------------721837373350705526688164684" - -POSTDATA = """-----------------------------721837373350705526688164684 -Content-Disposition: form-data; name="id" - -1234 ------------------------------721837373350705526688164684 -Content-Disposition: form-data; name="title" - ------------------------------721837373350705526688164684 -Content-Disposition: form-data; name="file"; filename="test.txt" -Content-Type: text/plain - -Testing 123. - ------------------------------721837373350705526688164684 -Content-Disposition: form-data; name="submit" - - Add\x20 ------------------------------721837373350705526688164684-- -""" - -POSTDATA_NON_ASCII = """-----------------------------721837373350705526688164684 -Content-Disposition: form-data; name="id" - -\xe7\xf1\x80 ------------------------------721837373350705526688164684 -""" - -# http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4 -BOUNDARY_W3 = "AaB03x" -POSTDATA_W3 = """--AaB03x -Content-Disposition: form-data; name="submit-name" - -Larry ---AaB03x -Content-Disposition: form-data; name="files" -Content-Type: multipart/mixed; boundary=BbC04y - ---BbC04y -Content-Disposition: file; filename="file1.txt" -Content-Type: text/plain - -... contents of file1.txt ... ---BbC04y -Content-Disposition: file; filename="file2.gif" -Content-Type: image/gif -Content-Transfer-Encoding: binary - -...contents of file2.gif... ---BbC04y-- ---AaB03x-- -""" +def test_main(): + run_unittest(CgiTests) if __name__ == '__main__': - unittest.main() + test_main() |