diff options
Diffstat (limited to 'Lib')
28 files changed, 866 insertions, 552 deletions
diff --git a/Lib/DocXMLRPCServer.py b/Lib/DocXMLRPCServer.py index 259fb18..86ed32b 100644 --- a/Lib/DocXMLRPCServer.py +++ b/Lib/DocXMLRPCServer.py @@ -227,6 +227,10 @@ class DocXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): Interpret all HTTP GET requests as requests for server documentation. """ + # Check that the path is legal + if not self.is_rpc_path_valid(): + self.report_404() + return response = self.server.generate_html_documentation() self.send_response(200) diff --git a/Lib/SimpleXMLRPCServer.py b/Lib/SimpleXMLRPCServer.py index 1d4f00f..0846a68 100644 --- a/Lib/SimpleXMLRPCServer.py +++ b/Lib/SimpleXMLRPCServer.py @@ -247,10 +247,10 @@ class SimpleXMLRPCDispatcher: of changing method dispatch behavior. """ - params, method = xmlrpclib.loads(data) - - # generate response try: + params, method = xmlrpclib.loads(data) + + # generate response if dispatch_method is not None: response = dispatch_method(method, params) else: @@ -423,6 +423,17 @@ class SimpleXMLRPCRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): XML-RPC requests. """ + # Class attribute listing the accessible path components; + # paths not on this list will result in a 404 error. + rpc_paths = ('/', '/RPC2') + + def is_rpc_path_valid(self): + if self.rpc_paths: + return self.path in self.rpc_paths + else: + # If .rpc_paths is empty, just assume all paths are legal + return True + def do_POST(self): """Handles the HTTP POST request. @@ -430,6 +441,11 @@ class SimpleXMLRPCRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): which are forwarded to the server's _dispatch method for handling. """ + # Check that the path is legal + if not self.is_rpc_path_valid(): + self.report_404() + return + try: # Get arguments by reading body of request. # We read this in chunks to avoid straining @@ -468,6 +484,18 @@ class SimpleXMLRPCRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): self.wfile.flush() self.connection.shutdown(1) + def report_404 (self): + # Report a 404 error + self.send_response(404) + response = 'No such page' + self.send_header("Content-type", "text/plain") + self.send_header("Content-length", str(len(response))) + self.end_headers() + self.wfile.write(response) + # shut down the connection + self.wfile.flush() + self.connection.shutdown(1) + def log_request(self, code='-', size='-'): """Selectively log an accepted request.""" diff --git a/Lib/aifc.py b/Lib/aifc.py index 781d77c..a5f86be 100644 --- a/Lib/aifc.py +++ b/Lib/aifc.py @@ -201,6 +201,8 @@ def _write_long(f, x): f.write(struct.pack('>L', x)) def _write_string(f, s): + if len(s) > 255: + raise ValueError("string exceeds maximum pstring length") f.write(chr(len(s))) f.write(s) if len(s) & 1 == 0: diff --git a/Lib/base64.py b/Lib/base64.py index 8914acc..c196cd8 100755 --- a/Lib/base64.py +++ b/Lib/base64.py @@ -126,7 +126,9 @@ _b32alphabet = { 8: 'I', 17: 'R', 26: '2', } -_b32tab = [v for v in _b32alphabet.values()] +_b32tab = _b32alphabet.items() +_b32tab.sort() +_b32tab = [v for k, v in _b32tab] _b32rev = dict([(v, long(k)) for k, v in _b32alphabet.items()]) diff --git a/Lib/doctest.py b/Lib/doctest.py index 857bc1a..d549163 100644 --- a/Lib/doctest.py +++ b/Lib/doctest.py @@ -1056,12 +1056,13 @@ class DocTestRunner: >>> tests = DocTestFinder().find(_TestClass) >>> runner = DocTestRunner(verbose=False) + >>> tests.sort(key = lambda test: test.name) >>> for test in tests: - ... print runner.run(test) - (0, 2) - (0, 1) - (0, 2) - (0, 2) + ... print test.name, '->', runner.run(test) + _TestClass -> (0, 2) + _TestClass.__init__ -> (0, 2) + _TestClass.get -> (0, 2) + _TestClass.square -> (0, 1) The `summarize` method prints a summary of all the test cases that have been run by the runner, and returns an aggregated `(f, t)` @@ -1869,7 +1870,8 @@ def testmod(m=None, name=None, globs=None, verbose=None, isprivate=None, def testfile(filename, module_relative=True, name=None, package=None, globs=None, verbose=None, report=True, optionflags=0, - extraglobs=None, raise_on_error=False, parser=DocTestParser()): + extraglobs=None, raise_on_error=False, parser=DocTestParser(), + encoding=None): """ Test examples in the given file. Return (#failures, #tests). @@ -1935,6 +1937,9 @@ def testfile(filename, module_relative=True, name=None, package=None, Optional keyword arg "parser" specifies a DocTestParser (or subclass) that should be used to extract tests from the files. + Optional keyword arg "encoding" specifies an encoding that should + be used to convert the file to unicode. + Advanced tomfoolery: testmod runs methods of a local instance of class doctest.Tester, then merges the results into (or creates) global Tester instance doctest.master. Methods of doctest.master @@ -1969,6 +1974,9 @@ def testfile(filename, module_relative=True, name=None, package=None, else: runner = DocTestRunner(verbose=verbose, optionflags=optionflags) + if encoding is not None: + text = text.decode(encoding) + # Read the file, convert it to a test, and run it. test = parser.get_doctest(text, globs, name, filename, 0) runner.run(test) @@ -2339,7 +2347,8 @@ class DocFileCase(DocTestCase): ) def DocFileTest(path, module_relative=True, package=None, - globs=None, parser=DocTestParser(), **options): + globs=None, parser=DocTestParser(), + encoding=None, **options): if globs is None: globs = {} else: @@ -2358,6 +2367,10 @@ def DocFileTest(path, module_relative=True, package=None, # Find the file and read it. name = os.path.basename(path) + # If an encoding is specified, use it to convert the file to unicode + if encoding is not None: + doc = doc.decode(encoding) + # Convert it to a test, and wrap it in a DocFileCase. test = parser.get_doctest(doc, globs, name, path, 0) return DocFileCase(test, **options) @@ -2414,6 +2427,9 @@ def DocFileSuite(*paths, **kw): parser A DocTestParser (or subclass) that should be used to extract tests from the files. + + encoding + An encoding that will be used to convert the files to unicode. """ suite = unittest.TestSuite() diff --git a/Lib/functools.py b/Lib/functools.py new file mode 100644 index 0000000..4935c9f --- /dev/null +++ b/Lib/functools.py @@ -0,0 +1,26 @@ +"""functools.py - Tools for working with functions +""" +# Python module wrapper for _functools C module +# to allow utilities written in Python to be added +# to the functools module. +# Written by Nick Coghlan <ncoghlan at gmail.com> +# Copyright (c) 2006 Python Software Foundation. + +from _functools import partial +__all__ = [ + "partial", +] + +# Still to come here (need to write tests and docs): +# update_wrapper - utility function to transfer basic function +# metadata to wrapper functions +# WRAPPER_ASSIGNMENTS & WRAPPER_UPDATES - defaults args to above +# (update_wrapper has been approved by BDFL) +# wraps - decorator factory equivalent to: +# def wraps(f): +# return partial(update_wrapper, wrapped=f) +# +# The wraps function makes it easy to avoid the bug that afflicts the +# decorator example in the python-dev email proposing the +# update_wrapper function: +# http://mail.python.org/pipermail/python-dev/2006-May/064775.html diff --git a/Lib/optparse.py b/Lib/optparse.py index 9ac987e..6b8f5d1 100644 --- a/Lib/optparse.py +++ b/Lib/optparse.py @@ -611,8 +611,10 @@ class Option: else: setattr(self, attr, None) if attrs: + attrs = attrs.keys() + attrs.sort() raise OptionError( - "invalid keyword arguments: %s" % ", ".join(attrs.keys()), + "invalid keyword arguments: %s" % ", ".join(attrs), self) @@ -1661,6 +1663,7 @@ def _match_abbrev(s, wordmap): raise BadOptionError(s) else: # More than one possible completion: ambiguous prefix. + possibilities.sort() raise AmbiguousOptionError(s, possibilities) diff --git a/Lib/pprint.py b/Lib/pprint.py index f77a0e2..19a3dc2 100644 --- a/Lib/pprint.py +++ b/Lib/pprint.py @@ -246,7 +246,7 @@ def _safe_repr(object, context, maxlevels, level): append = components.append level += 1 saferepr = _safe_repr - for k, v in object.iteritems(): + for k, v in sorted(object.items()): krepr, kreadable, krecur = saferepr(k, context, maxlevels, level) vrepr, vreadable, vrecur = saferepr(v, context, maxlevels, level) append("%s: %s" % (krepr, vrepr)) diff --git a/Lib/pyclbr.py b/Lib/pyclbr.py index 0812e22..0731224 100644 --- a/Lib/pyclbr.py +++ b/Lib/pyclbr.py @@ -42,7 +42,7 @@ Instances of this class have the following instance variables: import sys import imp import tokenize # Python tokenizer -from token import NAME, DEDENT, NEWLINE +from token import NAME, DEDENT, NEWLINE, OP from operator import itemgetter __all__ = ["readmodule", "readmodule_ex", "Class", "Function"] @@ -219,8 +219,10 @@ def _readmodule(module, path, inpackage=None): break elif token == ',' and level == 1: pass - else: + # only use NAME and OP (== dot) tokens for type name + elif tokentype in (NAME, OP) and level == 1: super.append(token) + # expressions in the base list are not supported inherit = names cur_class = Class(fullmodule, class_name, inherit, file, lineno) if not stack: diff --git a/Lib/test/crashers/dictresize_attack.py b/Lib/test/crashers/dictresize_attack.py deleted file mode 100644 index 1895791..0000000 --- a/Lib/test/crashers/dictresize_attack.py +++ /dev/null @@ -1,32 +0,0 @@ -# http://www.python.org/sf/1456209 - -# A dictresize() attack. If oldtable == mp->ma_smalltable then pure -# Python code can mangle with mp->ma_smalltable while it is being walked -# over. - -class X(object): - - def __hash__(self): - return 5 - - def __eq__(self, other): - if resizing: - d.clear() - return False - - -d = {} - -resizing = False - -d[X()] = 1 -d[X()] = 2 -d[X()] = 3 -d[X()] = 4 -d[X()] = 5 - -# now trigger a resize -resizing = True -d[9] = 6 - -# ^^^ I get Segmentation fault or Illegal instruction here. diff --git a/Lib/test/output/test_exceptions b/Lib/test/output/test_exceptions deleted file mode 100644 index 28a7aa8..0000000 --- a/Lib/test/output/test_exceptions +++ /dev/null @@ -1,52 +0,0 @@ -test_exceptions -5. Built-in exceptions -spam -AttributeError -spam -EOFError -spam -IOError -spam -ImportError -spam -IndexError -'spam' -KeyError -spam -KeyboardInterrupt -(not testable in a script) -spam -MemoryError -(not safe to test) -spam -NameError -spam -OverflowError -spam -RuntimeError -(not used any more?) -spam -SyntaxError -'continue' not supported inside 'finally' clause -ok -'continue' not properly in loop -ok -'continue' not properly in loop -ok -spam -IndentationError -spam -TabError -spam -SystemError -(hard to reproduce) -spam -SystemExit -spam -TypeError -spam -ValueError -spam -ZeroDivisionError -spam -Exception diff --git a/Lib/test/output/test_operations b/Lib/test/output/test_operations index 32eff3f..8a1bc2a 100644 --- a/Lib/test/output/test_operations +++ b/Lib/test/output/test_operations @@ -1,6 +1,21 @@ test_operations 3. Operations XXX Mostly not yet implemented -3.1 Dictionary lookups succeed even if __cmp__() raises an exception +3.1 Dictionary lookups fail if __cmp__() raises an exception raising error -No exception passed through. +d[x2] = 2: caught the RuntimeError outside +raising error +z = d[x2]: caught the RuntimeError outside +raising error +x2 in d: caught the RuntimeError outside +raising error +d.has_key(x2): caught the RuntimeError outside +raising error +d.get(x2): caught the RuntimeError outside +raising error +d.setdefault(x2, 42): caught the RuntimeError outside +raising error +d.pop(x2): caught the RuntimeError outside +raising error +d.update({x2: 2}): caught the RuntimeError outside +resize bugs not triggered. diff --git a/Lib/test/regrtest.py b/Lib/test/regrtest.py index 86961b0..314e7e1 100755 --- a/Lib/test/regrtest.py +++ b/Lib/test/regrtest.py @@ -513,6 +513,9 @@ def runtest(test, generate, verbose, quiet, testdir=None, huntrleaks=False): else: cfp = cStringIO.StringIO() if huntrleaks: + if not hasattr(sys, 'gettotalrefcount'): + raise Exception("Tracking reference leaks requires a debug build " + "of Python") refrep = open(huntrleaks[2], "a") try: save_stdout = sys.stdout diff --git a/Lib/test/string_tests.py b/Lib/test/string_tests.py index 489af20..aaa2dc2 100644 --- a/Lib/test/string_tests.py +++ b/Lib/test/string_tests.py @@ -106,10 +106,19 @@ class CommonTest(unittest.TestCase): self.checkequal(3, 'aaa', 'count', 'a') self.checkequal(0, 'aaa', 'count', 'b') self.checkequal(0, 'aaa', 'count', 'b') + self.checkequal(2, 'aaa', 'count', 'a', 1) + self.checkequal(0, 'aaa', 'count', 'a', 10) self.checkequal(1, 'aaa', 'count', 'a', -1) self.checkequal(3, 'aaa', 'count', 'a', -10) + self.checkequal(1, 'aaa', 'count', 'a', 0, 1) + self.checkequal(3, 'aaa', 'count', 'a', 0, 10) self.checkequal(2, 'aaa', 'count', 'a', 0, -1) self.checkequal(0, 'aaa', 'count', 'a', 0, -10) + self.checkequal(3, 'aaa', 'count', '', 1) + self.checkequal(1, 'aaa', 'count', '', 3) + self.checkequal(0, 'aaa', 'count', '', 10) + self.checkequal(2, 'aaa', 'count', '', -1) + self.checkequal(4, 'aaa', 'count', '', -10) self.checkraises(TypeError, 'hello', 'count') self.checkraises(TypeError, 'hello', 'count', 42) @@ -146,6 +155,10 @@ class CommonTest(unittest.TestCase): self.checkequal(9, 'abcdefghiabc', 'find', 'abc', 1) self.checkequal(-1, 'abcdefghiabc', 'find', 'def', 4) + self.checkequal(0, 'abc', 'find', '', 0) + self.checkequal(3, 'abc', 'find', '', 3) + self.checkequal(-1, 'abc', 'find', '', 4) + self.checkraises(TypeError, 'hello', 'find') self.checkraises(TypeError, 'hello', 'find', 42) @@ -180,6 +193,10 @@ class CommonTest(unittest.TestCase): self.checkequal(0, 'abcdefghiabc', 'rfind', 'abcd') self.checkequal(-1, 'abcdefghiabc', 'rfind', 'abcz') + self.checkequal(3, 'abc', 'rfind', '', 0) + self.checkequal(3, 'abc', 'rfind', '', 3) + self.checkequal(-1, 'abc', 'rfind', '', 4) + self.checkraises(TypeError, 'hello', 'rfind') self.checkraises(TypeError, 'hello', 'rfind', 42) @@ -477,12 +494,7 @@ class CommonTest(unittest.TestCase): # Operations on the empty string EQ("", "", "replace", "", "") - - #EQ("A", "", "replace", "", "A") - # That was the correct result; this is the result we actually get - # now (for str, but not for unicode): - #EQ("", "", "replace", "", "A") - + EQ("A", "", "replace", "", "A") EQ("", "", "replace", "A", "") EQ("", "", "replace", "A", "A") EQ("", "", "replace", "", "", 100) diff --git a/Lib/test/test_csv.py b/Lib/test/test_csv.py index 8511a5a..feb6ddf 100644 --- a/Lib/test/test_csv.py +++ b/Lib/test/test_csv.py @@ -875,7 +875,10 @@ Stonecutters Seafood and Chop House, Lemont, IL, 12/19/02, Week Back def test_delimiters(self): sniffer = csv.Sniffer() dialect = sniffer.sniff(self.sample3) - self.assertEqual(dialect.delimiter, "0") + # given that all three lines in sample3 are equal, + # I think that any character could have been 'guessed' as the + # delimiter, depending on dictionary order + self.assert_(dialect.delimiter in self.sample3) dialect = sniffer.sniff(self.sample3, delimiters="?,") self.assertEqual(dialect.delimiter, "?") dialect = sniffer.sniff(self.sample3, delimiters="/,") diff --git a/Lib/test/test_doctest.py b/Lib/test/test_doctest.py index 443c962..92d2d74 100644 --- a/Lib/test/test_doctest.py +++ b/Lib/test/test_doctest.py @@ -1937,9 +1937,10 @@ def test_DocFileSuite(): >>> import unittest >>> suite = doctest.DocFileSuite('test_doctest.txt', - ... 'test_doctest2.txt') + ... 'test_doctest2.txt', + ... 'test_doctest4.txt') >>> suite.run(unittest.TestResult()) - <unittest.TestResult run=2 errors=0 failures=2> + <unittest.TestResult run=3 errors=0 failures=3> The test files are looked for in the directory containing the calling module. A package keyword argument can be provided to @@ -1948,9 +1949,10 @@ def test_DocFileSuite(): >>> import unittest >>> suite = doctest.DocFileSuite('test_doctest.txt', ... 'test_doctest2.txt', + ... 'test_doctest4.txt', ... package='test') >>> suite.run(unittest.TestResult()) - <unittest.TestResult run=2 errors=0 failures=2> + <unittest.TestResult run=3 errors=0 failures=3> '/' should be used as a path separator. It will be converted to a native separator at run time: @@ -1995,19 +1997,21 @@ def test_DocFileSuite(): >>> suite = doctest.DocFileSuite('test_doctest.txt', ... 'test_doctest2.txt', + ... 'test_doctest4.txt', ... globs={'favorite_color': 'blue'}) >>> suite.run(unittest.TestResult()) - <unittest.TestResult run=2 errors=0 failures=1> + <unittest.TestResult run=3 errors=0 failures=2> In this case, we supplied a missing favorite color. You can provide doctest options: >>> suite = doctest.DocFileSuite('test_doctest.txt', ... 'test_doctest2.txt', + ... 'test_doctest4.txt', ... optionflags=doctest.DONT_ACCEPT_BLANKLINE, ... globs={'favorite_color': 'blue'}) >>> suite.run(unittest.TestResult()) - <unittest.TestResult run=2 errors=0 failures=2> + <unittest.TestResult run=3 errors=0 failures=3> And, you can provide setUp and tearDown functions: @@ -2025,9 +2029,10 @@ def test_DocFileSuite(): >>> suite = doctest.DocFileSuite('test_doctest.txt', ... 'test_doctest2.txt', + ... 'test_doctest4.txt', ... setUp=setUp, tearDown=tearDown) >>> suite.run(unittest.TestResult()) - <unittest.TestResult run=2 errors=0 failures=1> + <unittest.TestResult run=3 errors=0 failures=2> But the tearDown restores sanity: @@ -2060,6 +2065,17 @@ def test_DocFileSuite(): >>> suite.run(unittest.TestResult()) <unittest.TestResult run=1 errors=0 failures=0> + If the tests contain non-ASCII characters, we have to specify which + encoding the file is encoded with. We do so by using the `encoding` + parameter: + + >>> suite = doctest.DocFileSuite('test_doctest.txt', + ... 'test_doctest2.txt', + ... 'test_doctest4.txt', + ... encoding='utf-8') + >>> suite.run(unittest.TestResult()) + <unittest.TestResult run=3 errors=0 failures=2> + """ def test_trailing_space_in_test(): @@ -2266,6 +2282,32 @@ debugging): Traceback (most recent call last): UnexpectedException: ... >>> doctest.master = None # Reset master. + +If the tests contain non-ASCII characters, the tests might fail, since +it's unknown which encoding is used. The encoding can be specified +using the optional keyword argument `encoding`: + + >>> doctest.testfile('test_doctest4.txt') # doctest: +ELLIPSIS + ********************************************************************** + File "...", line 7, in test_doctest4.txt + Failed example: + u'...' + Expected: + u'f\xf6\xf6' + Got: + u'f\xc3\xb6\xc3\xb6' + ********************************************************************** + ... + ********************************************************************** + 1 items had failures: + 2 of 4 in test_doctest4.txt + ***Test Failed*** 2 failures. + (2, 4) + >>> doctest.master = None # Reset master. + + >>> doctest.testfile('test_doctest4.txt', encoding='utf-8') + (0, 4) + >>> doctest.master = None # Reset master. """ # old_test1, ... used to live in doctest.py, but cluttered it. Note diff --git a/Lib/test/test_doctest4.txt b/Lib/test/test_doctest4.txt new file mode 100644 index 0000000..a219d16 --- /dev/null +++ b/Lib/test/test_doctest4.txt @@ -0,0 +1,17 @@ +This is a sample doctest in a text file that contains non-ASCII characters. +This file is encoded using UTF-8. + +In order to get this test to pass, we have to manually specify the +encoding. + + >>> u'föö' + u'f\xf6\xf6' + + >>> u'bąr' + u'b\u0105r' + + >>> 'föö' + 'f\xc3\xb6\xc3\xb6' + + >>> 'bąr' + 'b\xc4\x85r' diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py index 8f995f7..ebab913 100644 --- a/Lib/test/test_exceptions.py +++ b/Lib/test/test_exceptions.py @@ -1,303 +1,310 @@ # Python test set -- part 5, built-in exceptions -from test.test_support import TestFailed, TESTFN, unlink -from types import ClassType +from test.test_support import TESTFN, unlink, run_unittest import warnings import sys, traceback, os +import unittest -print '5. Built-in exceptions' # XXX This is not really enough, each *operation* should be tested! -# Reloading the built-in exceptions module failed prior to Py2.2, while it -# should act the same as reloading built-in sys. -try: - import exceptions - reload(exceptions) -except ImportError, e: - raise TestFailed, e - -def test_raise_catch(exc): - try: - raise exc, "spam" - except exc, err: - buf = str(err) - try: - raise exc("spam") - except exc, err: - buf = str(err) - print buf - -def r(thing): - test_raise_catch(thing) - print getattr(thing, '__name__', thing) - -r(AttributeError) -import sys -try: x = sys.undefined_attribute -except AttributeError: pass - -r(EOFError) -import sys -fp = open(TESTFN, 'w') -fp.close() -fp = open(TESTFN, 'r') -savestdin = sys.stdin -try: - try: - import marshal - marshal.loads('') - except EOFError: - pass -finally: - sys.stdin = savestdin - fp.close() - -r(IOError) -try: open('this file does not exist', 'r') -except IOError: pass - -r(ImportError) -try: import undefined_module -except ImportError: pass - -r(IndexError) -x = [] -try: a = x[10] -except IndexError: pass - -r(KeyError) -x = {} -try: a = x['key'] -except KeyError: pass - -r(KeyboardInterrupt) -print '(not testable in a script)' - -r(MemoryError) -print '(not safe to test)' - -r(NameError) -try: x = undefined_variable -except NameError: pass - -r(OverflowError) -x = 1 -for dummy in range(128): - x += x # this simply shouldn't blow up - -r(RuntimeError) -print '(not used any more?)' - -r(SyntaxError) -try: exec '/\n' -except SyntaxError: pass - -# make sure the right exception message is raised for each of these -# code fragments: - -def ckmsg(src, msg): - try: - compile(src, '<fragment>', 'exec') - except SyntaxError, e: - print e.msg - if e.msg == msg: - print "ok" - else: - print "expected:", msg - else: - print "failed to get expected SyntaxError" - -s = '''\ -while 1: - try: - pass - finally: - continue -''' -if sys.platform.startswith('java'): - print "'continue' not supported inside 'finally' clause" - print "ok" -else: - ckmsg(s, "'continue' not supported inside 'finally' clause") -s = '''\ -try: - continue -except: - pass -''' -ckmsg(s, "'continue' not properly in loop") -ckmsg("continue\n", "'continue' not properly in loop") - -r(IndentationError) - -r(TabError) -# can only be tested under -tt, and is the only test for -tt -#try: compile("try:\n\t1/0\n \t1/0\nfinally:\n pass\n", '<string>', 'exec') -#except TabError: pass -#else: raise TestFailed - -r(SystemError) -print '(hard to reproduce)' - -r(SystemExit) -import sys -try: sys.exit(0) -except SystemExit: pass - -r(TypeError) -try: [] + () -except TypeError: pass - -r(ValueError) -try: x = chr(10000) -except ValueError: pass - -r(ZeroDivisionError) -try: x = 1/0 -except ZeroDivisionError: pass - -r(Exception) -try: x = 1/0 -except Exception, e: pass - -# test that setting an exception at the C level works even if the -# exception object can't be constructed. - -class BadException(Exception): - def __init__(self): - raise RuntimeError, "can't instantiate BadException" - -# Exceptions must inherit from BaseException, raising invalid exception -# should instead raise SystemError -class InvalidException: - pass - -def test_capi1(): - import _testcapi - try: - _testcapi.raise_exception(BadException, 1) - except TypeError, err: - exc, err, tb = sys.exc_info() - co = tb.tb_frame.f_code - assert co.co_name == "test_capi1" - assert co.co_filename.endswith('test_exceptions'+os.extsep+'py') - else: - print "Expected exception" - -def test_capi2(): - import _testcapi - try: - _testcapi.raise_exception(BadException, 0) - except RuntimeError, err: - exc, err, tb = sys.exc_info() - co = tb.tb_frame.f_code - assert co.co_name == "__init__" - assert co.co_filename.endswith('test_exceptions'+os.extsep+'py') - co2 = tb.tb_frame.f_back.f_code - assert co2.co_name == "test_capi2" - else: - print "Expected exception" - -def test_capi3(): - import _testcapi - try: - _testcapi.raise_exception(InvalidException, 1) - except SystemError: - pass - except InvalidException: - raise AssertionError("Managed to raise InvalidException"); - else: - print "Expected SystemError exception" - - -if not sys.platform.startswith('java'): - test_capi1() - test_capi2() - test_capi3() - -unlink(TESTFN) - -# test that exception attributes are happy. -try: str(u'Hello \u00E1') -except Exception, e: sampleUnicodeEncodeError = e -try: unicode('\xff') -except Exception, e: sampleUnicodeDecodeError = e -exceptionList = [ - ( BaseException, (), { 'message' : '', 'args' : () }), - ( BaseException, (1, ), { 'message' : 1, 'args' : ( 1, ) }), - ( BaseException, ('foo', ), { 'message' : 'foo', 'args' : ( 'foo', ) }), - ( BaseException, ('foo', 1), { 'message' : '', 'args' : ( 'foo', 1 ) }), - ( SystemExit, ('foo',), { 'message' : 'foo', 'args' : ( 'foo', ), - 'code' : 'foo' }), - ( IOError, ('foo',), { 'message' : 'foo', 'args' : ( 'foo', ), }), - ( IOError, ('foo', 'bar'), { 'message' : '', - 'args' : ('foo', 'bar'), }), - ( IOError, ('foo', 'bar', 'baz'), - { 'message' : '', 'args' : ('foo', 'bar'), }), - ( EnvironmentError, ('errnoStr', 'strErrorStr', 'filenameStr'), - { 'message' : '', 'args' : ('errnoStr', 'strErrorStr'), - 'strerror' : 'strErrorStr', - 'errno' : 'errnoStr', 'filename' : 'filenameStr' }), - ( EnvironmentError, (1, 'strErrorStr', 'filenameStr'), - { 'message' : '', 'args' : (1, 'strErrorStr'), - 'strerror' : 'strErrorStr', 'errno' : 1, - 'filename' : 'filenameStr' }), - ( SyntaxError, ('msgStr',), - { 'message' : 'msgStr', 'args' : ('msgStr', ), - 'print_file_and_line' : None, 'msg' : 'msgStr', - 'filename' : None, 'lineno' : None, 'offset' : None, - 'text' : None }), - ( SyntaxError, ('msgStr', ('filenameStr', 'linenoStr', 'offsetStr', - 'textStr')), - { 'message' : '', 'args' : ('msgStr', ('filenameStr', - 'linenoStr', 'offsetStr', 'textStr' )), - 'print_file_and_line' : None, 'msg' : 'msgStr', - 'filename' : 'filenameStr', 'lineno' : 'linenoStr', - 'offset' : 'offsetStr', 'text' : 'textStr' }), - ( SyntaxError, ('msgStr', 'filenameStr', 'linenoStr', 'offsetStr', - 'textStr', 'print_file_and_lineStr'), - { 'message' : '', 'args' : ('msgStr', 'filenameStr', - 'linenoStr', 'offsetStr', 'textStr', - 'print_file_and_lineStr'), - 'print_file_and_line' : None, 'msg' : 'msgStr', - 'filename' : None, 'lineno' : None, 'offset' : None, - 'text' : None }), - ( UnicodeError, (), - { 'message' : '', 'args' : (), }), - ( sampleUnicodeEncodeError, - { 'message' : '', 'args' : ('ascii', u'Hello \xe1', 6, 7, - 'ordinal not in range(128)'), - 'encoding' : 'ascii', 'object' : u'Hello \xe1', - 'start' : 6, 'reason' : 'ordinal not in range(128)' }), - ( sampleUnicodeDecodeError, - { 'message' : '', 'args' : ('ascii', '\xff', 0, 1, - 'ordinal not in range(128)'), - 'encoding' : 'ascii', 'object' : '\xff', - 'start' : 0, 'reason' : 'ordinal not in range(128)' }), - ( UnicodeTranslateError, (u"\u3042", 0, 1, "ouch"), - { 'message' : '', 'args' : (u'\u3042', 0, 1, 'ouch'), - 'object' : u'\u3042', 'reason' : 'ouch', - 'start' : 0, 'end' : 1 }), +class ExceptionTests(unittest.TestCase): + + def testReload(self): + # Reloading the built-in exceptions module failed prior to Py2.2, while it + # should act the same as reloading built-in sys. + try: + import exceptions + reload(exceptions) + except ImportError, e: + self.fail("reloading exceptions: %s" % e) + + def raise_catch(self, exc, excname): + try: + raise exc, "spam" + except exc, err: + buf1 = str(err) + try: + raise exc("spam") + except exc, err: + buf2 = str(err) + self.assertEquals(buf1, buf2) + self.assertEquals(exc.__name__, excname) + + def testRaising(self): + self.raise_catch(AttributeError, "AttributeError") + self.assertRaises(AttributeError, getattr, sys, "undefined_attribute") + + self.raise_catch(EOFError, "EOFError") + fp = open(TESTFN, 'w') + fp.close() + fp = open(TESTFN, 'r') + savestdin = sys.stdin + try: + try: + import marshal + marshal.loads('') + except EOFError: + pass + finally: + sys.stdin = savestdin + fp.close() + unlink(TESTFN) + + self.raise_catch(IOError, "IOError") + self.assertRaises(IOError, open, 'this file does not exist', 'r') + + self.raise_catch(ImportError, "ImportError") + self.assertRaises(ImportError, __import__, "undefined_module") + + self.raise_catch(IndexError, "IndexError") + x = [] + self.assertRaises(IndexError, x.__getitem__, 10) + + self.raise_catch(KeyError, "KeyError") + x = {} + self.assertRaises(KeyError, x.__getitem__, 'key') + + self.raise_catch(KeyboardInterrupt, "KeyboardInterrupt") + + self.raise_catch(MemoryError, "MemoryError") + + self.raise_catch(NameError, "NameError") + try: x = undefined_variable + except NameError: pass + + self.raise_catch(OverflowError, "OverflowError") + x = 1 + for dummy in range(128): + x += x # this simply shouldn't blow up + + self.raise_catch(RuntimeError, "RuntimeError") + + self.raise_catch(SyntaxError, "SyntaxError") + try: exec '/\n' + except SyntaxError: pass + + self.raise_catch(IndentationError, "IndentationError") + + self.raise_catch(TabError, "TabError") + # can only be tested under -tt, and is the only test for -tt + #try: compile("try:\n\t1/0\n \t1/0\nfinally:\n pass\n", '<string>', 'exec') + #except TabError: pass + #else: self.fail("TabError not raised") + + self.raise_catch(SystemError, "SystemError") + + self.raise_catch(SystemExit, "SystemExit") + self.assertRaises(SystemExit, sys.exit, 0) + + self.raise_catch(TypeError, "TypeError") + try: [] + () + except TypeError: pass + + self.raise_catch(ValueError, "ValueError") + self.assertRaises(ValueError, chr, 10000) + + self.raise_catch(ZeroDivisionError, "ZeroDivisionError") + try: x = 1/0 + except ZeroDivisionError: pass + + self.raise_catch(Exception, "Exception") + try: x = 1/0 + except Exception, e: pass + + def testSyntaxErrorMessage(self): + # make sure the right exception message is raised for each of + # these code fragments + + def ckmsg(src, msg): + try: + compile(src, '<fragment>', 'exec') + except SyntaxError, e: + if e.msg != msg: + self.fail("expected %s, got %s" % (msg, e.msg)) + else: + self.fail("failed to get expected SyntaxError") + + s = '''while 1: + try: + pass + finally: + continue''' + + if not sys.platform.startswith('java'): + ckmsg(s, "'continue' not supported inside 'finally' clause") + + s = '''if 1: + try: + continue + except: + pass''' + + ckmsg(s, "'continue' not properly in loop") + ckmsg("continue\n", "'continue' not properly in loop") + + def testSettingException(self): + # test that setting an exception at the C level works even if the + # exception object can't be constructed. + + class BadException(Exception): + def __init__(self_): + raise RuntimeError, "can't instantiate BadException" + + class InvalidException: + pass + + def test_capi1(): + import _testcapi + try: + _testcapi.raise_exception(BadException, 1) + except TypeError, err: + exc, err, tb = sys.exc_info() + co = tb.tb_frame.f_code + self.assertEquals(co.co_name, "test_capi1") + self.assert_(co.co_filename.endswith('test_exceptions'+os.extsep+'py')) + else: + self.fail("Expected exception") + + def test_capi2(): + import _testcapi + try: + _testcapi.raise_exception(BadException, 0) + except RuntimeError, err: + exc, err, tb = sys.exc_info() + co = tb.tb_frame.f_code + self.assertEquals(co.co_name, "__init__") + self.assert_(co.co_filename.endswith('test_exceptions'+os.extsep+'py')) + co2 = tb.tb_frame.f_back.f_code + self.assertEquals(co2.co_name, "test_capi2") + else: + self.fail("Expected exception") + + def test_capi3(): + import _testcapi + self.assertRaises(SystemError, _testcapi.raise_exception, + InvalidException, 1) + + if not sys.platform.startswith('java'): + test_capi1() + test_capi2() + test_capi3() + + def testAttributes(self): + # test that exception attributes are happy + try: str(u'Hello \u00E1') + except Exception, e: sampleUnicodeEncodeError = e + + try: unicode('\xff') + except Exception, e: sampleUnicodeDecodeError = e + + exceptionList = [ + (BaseException, (), {'message' : '', 'args' : ()}), + (BaseException, (1, ), {'message' : 1, 'args' : (1,)}), + (BaseException, ('foo',), + {'message' : 'foo', 'args' : ('foo',)}), + (BaseException, ('foo', 1), + {'message' : '', 'args' : ('foo', 1)}), + (SystemExit, ('foo',), + {'message' : 'foo', 'args' : ('foo',), 'code' : 'foo'}), + (IOError, ('foo',), + {'message' : 'foo', 'args' : ('foo',)}), + (IOError, ('foo', 'bar'), + {'message' : '', 'args' : ('foo', 'bar')}), + (IOError, ('foo', 'bar', 'baz'), + {'message' : '', 'args' : ('foo', 'bar')}), + (EnvironmentError, ('errnoStr', 'strErrorStr', 'filenameStr'), + {'message' : '', 'args' : ('errnoStr', 'strErrorStr'), + 'strerror' : 'strErrorStr', 'errno' : 'errnoStr', + 'filename' : 'filenameStr'}), + (EnvironmentError, (1, 'strErrorStr', 'filenameStr'), + {'message' : '', 'args' : (1, 'strErrorStr'), 'errno' : 1, + 'strerror' : 'strErrorStr', 'filename' : 'filenameStr'}), + (SyntaxError, ('msgStr',), + {'message' : 'msgStr', 'args' : ('msgStr',), 'text' : None, + 'print_file_and_line' : None, 'msg' : 'msgStr', + 'filename' : None, 'lineno' : None, 'offset' : None}), + (SyntaxError, ('msgStr', ('filenameStr', 'linenoStr', 'offsetStr', + 'textStr')), + {'message' : '', 'offset' : 'offsetStr', 'text' : 'textStr', + 'args' : ('msgStr', ('filenameStr', 'linenoStr', + 'offsetStr', 'textStr')), + 'print_file_and_line' : None, 'msg' : 'msgStr', + 'filename' : 'filenameStr', 'lineno' : 'linenoStr'}), + (SyntaxError, ('msgStr', 'filenameStr', 'linenoStr', 'offsetStr', + 'textStr', 'print_file_and_lineStr'), + {'message' : '', 'text' : None, + 'args' : ('msgStr', 'filenameStr', 'linenoStr', 'offsetStr', + 'textStr', 'print_file_and_lineStr'), + 'print_file_and_line' : None, 'msg' : 'msgStr', + 'filename' : None, 'lineno' : None, 'offset' : None}), + (UnicodeError, (), {'message' : '', 'args' : (),}), + (sampleUnicodeEncodeError, + {'message' : '', 'args' : ('ascii', u'Hello \xe1', 6, 7, + 'ordinal not in range(128)'), + 'encoding' : 'ascii', 'object' : u'Hello \xe1', + 'start' : 6, 'reason' : 'ordinal not in range(128)'}), + (sampleUnicodeDecodeError, + {'message' : '', 'args' : ('ascii', '\xff', 0, 1, + 'ordinal not in range(128)'), + 'encoding' : 'ascii', 'object' : '\xff', + 'start' : 0, 'reason' : 'ordinal not in range(128)'}), + (UnicodeTranslateError, (u"\u3042", 0, 1, "ouch"), + {'message' : '', 'args' : (u'\u3042', 0, 1, 'ouch'), + 'object' : u'\u3042', 'reason' : 'ouch', + 'start' : 0, 'end' : 1}), ] -try: - exceptionList.append( - ( WindowsError, (1, 'strErrorStr', 'filenameStr'), - { 'message' : '', 'args' : (1, 'strErrorStr'), - 'strerror' : 'strErrorStr', - 'errno' : 22, 'filename' : 'filenameStr', - 'winerror' : 1 })) -except NameError: pass - -for args in exceptionList: - expected = args[-1] - try: - if len(args) == 2: raise args[0] - else: raise apply(args[0], args[1]) - except BaseException, e: - for checkArgName in expected.keys(): - if repr(getattr(e, checkArgName)) != repr(expected[checkArgName]): - raise TestFailed('Checking exception arguments, exception ' - '"%s", attribute "%s" expected %s got %s.' % - ( repr(e), checkArgName, - repr(expected[checkArgName]), - repr(getattr(e, checkArgName)) )) + try: + exceptionList.append( + (WindowsError, (1, 'strErrorStr', 'filenameStr'), + {'message' : '', 'args' : (1, 'strErrorStr'), + 'strerror' : 'strErrorStr', 'winerror' : 1, + 'errno' : 22, 'filename' : 'filenameStr'}) + ) + except NameError: pass + + import pickle, random + + for args in exceptionList: + expected = args[-1] + try: + exc = args[0] + if len(args) == 2: raise exc + else: raise exc(*args[1]) + except BaseException, e: + if (e is not exc and # needed for sampleUnicode errors + type(e) is not exc): + raise + # Verify no ref leaks in Exc_str() + s = str(e) + for checkArgName in expected: + self.assertEquals(repr(getattr(e, checkArgName)), + repr(expected[checkArgName]), + 'exception "%s", attribute "%s"' % + (repr(e), checkArgName)) + + # test for pickling support + new = pickle.loads(pickle.dumps(e, random.randint(0, 2))) + for checkArgName in expected: + self.assertEquals(repr(getattr(e, checkArgName)), + repr(expected[checkArgName]), + 'pickled exception "%s", attribute "%s' % + (repr(e), checkArgName)) + + def testKeywordArgs(self): + # test that builtin exception don't take keyword args, + # but user-defined subclasses can if they want + self.assertRaises(TypeError, BaseException, a=1) + + class DerivedException(BaseException): + def __init__(self, fancy_arg): + BaseException.__init__(self) + self.fancy_arg = fancy_arg + + x = DerivedException(fancy_arg=42) + self.assertEquals(x.fancy_arg, 42) + +def test_main(): + run_unittest(ExceptionTests) + +if __name__ == '__main__': + test_main() diff --git a/Lib/test/test_functional.py b/Lib/test/test_functools.py index 5078a2e..609e8f4 100644 --- a/Lib/test/test_functional.py +++ b/Lib/test/test_functools.py @@ -1,4 +1,4 @@ -import functional +import functools import unittest from test import test_support from weakref import proxy @@ -21,7 +21,7 @@ def capture(*args, **kw): class TestPartial(unittest.TestCase): - thetype = functional.partial + thetype = functools.partial def test_basic_examples(self): p = self.thetype(capture, 1, 2, a=10, b=20) @@ -140,7 +140,7 @@ class TestPartial(unittest.TestCase): join = self.thetype(''.join) self.assertEqual(join(data), '0123456789') -class PartialSubclass(functional.partial): +class PartialSubclass(functools.partial): pass class TestPartialSubclass(TestPartial): diff --git a/Lib/test/test_itertools.py b/Lib/test/test_itertools.py index c4ed3bc..b2a9b55 100644 --- a/Lib/test/test_itertools.py +++ b/Lib/test/test_itertools.py @@ -766,7 +766,7 @@ Samuele >>> from operator import itemgetter >>> d = dict(a=1, b=2, c=1, d=2, e=1, f=2, g=3) ->>> di = sorted(d.iteritems(), key=itemgetter(1)) +>>> di = sorted(sorted(d.iteritems()), key=itemgetter(1)) >>> for k, g in groupby(di, itemgetter(1)): ... print k, map(itemgetter(0), g) ... diff --git a/Lib/test/test_operations.py b/Lib/test/test_operations.py index b599c9d..fafc062 100644 --- a/Lib/test/test_operations.py +++ b/Lib/test/test_operations.py @@ -5,27 +5,16 @@ print '3. Operations' print 'XXX Mostly not yet implemented' -print '3.1 Dictionary lookups succeed even if __cmp__() raises an exception' - -# SourceForge bug #112558: -# http://sourceforge.net/bugs/?func=detailbug&bug_id=112558&group_id=5470 +print '3.1 Dictionary lookups fail if __cmp__() raises an exception' class BadDictKey: - already_printed_raising_error = 0 def __hash__(self): return hash(self.__class__) def __cmp__(self, other): if isinstance(other, self.__class__): - if not BadDictKey.already_printed_raising_error: - # How many times __cmp__ gets called depends on the hash - # code and the internals of the dict implementation; we - # know it will be called at least once, but that's it. - # already_printed_raising_error makes sure the expected- - # output file prints the msg at most once. - BadDictKey.already_printed_raising_error = 1 - print "raising error" + print "raising error" raise RuntimeError, "gotcha" return other @@ -33,8 +22,21 @@ d = {} x1 = BadDictKey() x2 = BadDictKey() d[x1] = 1 -d[x2] = 2 -print "No exception passed through." +for stmt in ['d[x2] = 2', + 'z = d[x2]', + 'x2 in d', + 'd.has_key(x2)', + 'd.get(x2)', + 'd.setdefault(x2, 42)', + 'd.pop(x2)', + 'd.update({x2: 2})']: + try: + exec stmt + except RuntimeError: + print "%s: caught the RuntimeError outside" % (stmt,) + else: + print "%s: No exception passed through!" # old CPython behavior + # Dict resizing bug, found by Jack Jansen in 2.2 CVS development. # This version got an assert failure in debug build, infinite loop in @@ -50,3 +52,27 @@ for i in range(5): del d[i] for i in range(5, 9): # i==8 was the problem d[i] = i + + +# Another dict resizing bug (SF bug #1456209). +# This caused Segmentation faults or Illegal instructions. + +class X(object): + def __hash__(self): + return 5 + def __eq__(self, other): + if resizing: + d.clear() + return False +d = {} +resizing = False +d[X()] = 1 +d[X()] = 2 +d[X()] = 3 +d[X()] = 4 +d[X()] = 5 +# now trigger a resize +resizing = True +d[9] = 6 + +print 'resize bugs not triggered.' diff --git a/Lib/test/test_optparse.py b/Lib/test/test_optparse.py index 991c06d..79df906 100644 --- a/Lib/test/test_optparse.py +++ b/Lib/test/test_optparse.py @@ -230,7 +230,7 @@ class TestOptionChecks(BaseTest): def test_attr_invalid(self): self.assertOptionError( - "option -b: invalid keyword arguments: foo, bar", + "option -b: invalid keyword arguments: bar, foo", ["-b"], {'foo': None, 'bar': None}) def test_action_invalid(self): @@ -718,9 +718,8 @@ class TestStandard(BaseTest): def test_ambiguous_option(self): self.parser.add_option("--foz", action="store", type="string", dest="foo") - possibilities = ", ".join({"--foz": None, "--foo": None}.keys()) self.assertParseFail(["--f=bar"], - "ambiguous option: --f (%s?)" % possibilities) + "ambiguous option: --f (--foo, --foz?)") def test_short_and_long_option_split(self): @@ -1537,10 +1536,9 @@ class TestMatchAbbrev(BaseTest): def test_match_abbrev_error(self): s = "--f" wordmap = {"--foz": None, "--foo": None, "--fie": None} - possibilities = ", ".join(wordmap.keys()) self.assertRaises( _match_abbrev, (s, wordmap), None, - BadOptionError, "ambiguous option: --f (%s?)" % possibilities) + BadOptionError, "ambiguous option: --f (--fie, --foo, --foz?)") class TestParseNumber(BaseTest): diff --git a/Lib/test/test_pprint.py b/Lib/test/test_pprint.py index 27d6b52..09ba268 100644 --- a/Lib/test/test_pprint.py +++ b/Lib/test/test_pprint.py @@ -11,16 +11,21 @@ except NameError: # list, tuple and dict subclasses that do or don't overwrite __repr__ class list2(list): pass + class list3(list): def __repr__(self): return list.__repr__(self) + class tuple2(tuple): pass + class tuple3(tuple): def __repr__(self): return tuple.__repr__(self) + class dict2(dict): pass + class dict3(dict): def __repr__(self): return dict.__repr__(self) @@ -101,7 +106,13 @@ class QueryTestCase(unittest.TestCase): def test_same_as_repr(self): # Simple objects, small containers and classes that overwrite __repr__ - # For those the result should be the same as repr() + # For those the result should be the same as repr(). + # Ahem. The docs don't say anything about that -- this appears to + # be testing an implementation quirk. Starting in Python 2.5, it's + # not true for dicts: pprint always sorts dicts by key now; before, + # it sorted a dict display if and only if the display required + # multiple lines. For that reason, dicts with more than one element + # aren't tested here. verify = self.assert_ for simple in (0, 0L, 0+0j, 0.0, "", uni(""), (), tuple2(), tuple3(), @@ -112,9 +123,7 @@ class QueryTestCase(unittest.TestCase): (1,2), [3,4], {5: 6, 7: 8}, tuple2((1,2)), tuple3((1,2)), tuple3(range(100)), [3,4], list2([3,4]), list3([3,4]), list3(range(100)), - {5: 6, 7: 8}, dict2({5: 6, 7: 8}), dict3({5: 6, 7: 8}), - dict3([(x,x) for x in range(100)]), - {"xy\tab\n": (3,), 5: [[]], (): {}}, + {5: 6, 7: 8}, dict2({5: 6}), dict3({5: 6}), range(10, -11, -1) ): native = repr(simple) @@ -160,6 +169,24 @@ class QueryTestCase(unittest.TestCase): for type in [list, list2]: self.assertEqual(pprint.pformat(type(o), indent=4), exp) + def test_sorted_dict(self): + # Starting in Python 2.5, pprint sorts dict displays by key regardless + # of how small the dictionary may be. + # Before the change, on 32-bit Windows pformat() gave order + # 'a', 'c', 'b' here, so this test failed. + d = {'a': 1, 'b': 1, 'c': 1} + self.assertEqual(pprint.pformat(d), "{'a': 1, 'b': 1, 'c': 1}") + self.assertEqual(pprint.pformat([d, d]), + "[{'a': 1, 'b': 1, 'c': 1}, {'a': 1, 'b': 1, 'c': 1}]") + + # The next one is kind of goofy. The sorted order depends on the + # alphabetic order of type names: "int" < "str" < "tuple". Before + # Python 2.5, this was in the test_same_as_repr() test. It's worth + # keeping around for now because it's one of few tests of pprint + # against a crazy mix of types. + self.assertEqual(pprint.pformat({"xy\tab\n": (3,), 5: [[]], (): {}}), + r"{5: [[]], 'xy\tab\n': (3,), (): {}}") + def test_subclassing(self): o = {'names with spaces': 'should be presented using repr()', 'others.should.not.be': 'like.this'} diff --git a/Lib/test/test_repr.py b/Lib/test/test_repr.py index 9128585..1dfa282 100644 --- a/Lib/test/test_repr.py +++ b/Lib/test/test_repr.py @@ -5,6 +5,7 @@ import sys import os +import shutil import unittest from test.test_support import run_unittest @@ -198,8 +199,10 @@ class LongReprTest(unittest.TestCase): self.pkgname = os.path.join(longname) self.subpkgname = os.path.join(longname, longname) # Make the package and subpackage + shutil.rmtree(self.pkgname, ignore_errors=True) os.mkdir(self.pkgname) touch(os.path.join(self.pkgname, '__init__'+os.extsep+'py')) + shutil.rmtree(self.subpkgname, ignore_errors=True) os.mkdir(self.subpkgname) touch(os.path.join(self.subpkgname, '__init__'+os.extsep+'py')) # Remember where we are diff --git a/Lib/test/test_struct.py b/Lib/test/test_struct.py index 7981a52..af835f7 100644 --- a/Lib/test/test_struct.py +++ b/Lib/test/test_struct.py @@ -2,7 +2,7 @@ from test.test_support import TestFailed, verbose, verify import test.test_support import struct import array -import unittest +import warnings import sys ISBIGENDIAN = sys.byteorder == "big" @@ -10,7 +10,14 @@ del sys verify((struct.pack('=i', 1)[0] == chr(0)) == ISBIGENDIAN, "bigendian determination appears wrong") -PY_STRUCT_RANGE_CHECKING = 1 +try: + import _struct +except ImportError: + PY_STRUCT_RANGE_CHECKING = 0 + PY_STRUCT_OVERFLOW_MASKING = 1 +else: + PY_STRUCT_RANGE_CHECKING = getattr(_struct, '_PY_STRUCT_RANGE_CHECKING', 0) + PY_STRUCT_OVERFLOW_MASKING = getattr(_struct, '_PY_STRUCT_OVERFLOW_MASKING', 0) def string_reverse(s): chars = list(s) @@ -35,12 +42,39 @@ def simple_err(func, *args): def any_err(func, *args): try: func(*args) - except (struct.error, OverflowError, TypeError): + except (struct.error, TypeError): pass else: raise TestFailed, "%s%s did not raise error" % ( func.__name__, args) +def deprecated_err(func, *args): + # The `warnings` module doesn't have an advertised way to restore + # its filter list. Cheat. + save_warnings_filters = warnings.filters[:] + # Grrr, we need this function to warn every time. Without removing + # the warningregistry, running test_tarfile then test_struct would fail + # on 64-bit platforms. + globals = func.func_globals + if '__warningregistry__' in globals: + del globals['__warningregistry__'] + warnings.filterwarnings("error", r"""^struct.*""", DeprecationWarning) + warnings.filterwarnings("error", r""".*format requires.*""", + DeprecationWarning) + try: + try: + func(*args) + except (struct.error, TypeError): + pass + except DeprecationWarning: + if not PY_STRUCT_OVERFLOW_MASKING: + raise TestFailed, "%s%s expected to raise struct.error" % ( + func.__name__, args) + else: + raise TestFailed, "%s%s did not raise error" % ( + func.__name__, args) + finally: + warnings.filters[:] = save_warnings_filters[:] simple_err(struct.calcsize, 'Z') @@ -272,8 +306,8 @@ class IntTester: if verbose: print "Skipping buggy range check for code", code else: - any_err(pack, ">" + code, x) - any_err(pack, "<" + code, x) + deprecated_err(pack, ">" + code, x) + deprecated_err(pack, "<" + code, x) # Much the same for unsigned. code = self.unsigned_code @@ -327,8 +361,8 @@ class IntTester: if verbose: print "Skipping buggy range check for code", code else: - any_err(pack, ">" + code, x) - any_err(pack, "<" + code, x) + deprecated_err(pack, ">" + code, x) + deprecated_err(pack, "<" + code, x) def run(self): from random import randrange @@ -448,91 +482,98 @@ def test_1229380(): for endian in ('', '>', '<'): for cls in (int, long): for fmt in ('B', 'H', 'I', 'L'): - any_err(struct.pack, endian + fmt, cls(-1)) + deprecated_err(struct.pack, endian + fmt, cls(-1)) - any_err(struct.pack, endian + 'B', cls(300)) - any_err(struct.pack, endian + 'H', cls(70000)) + deprecated_err(struct.pack, endian + 'B', cls(300)) + deprecated_err(struct.pack, endian + 'H', cls(70000)) - any_err(struct.pack, endian + 'I', sys.maxint * 4L) - any_err(struct.pack, endian + 'L', sys.maxint * 4L) + deprecated_err(struct.pack, endian + 'I', sys.maxint * 4L) + deprecated_err(struct.pack, endian + 'L', sys.maxint * 4L) if PY_STRUCT_RANGE_CHECKING: test_1229380() -class PackBufferTestCase(unittest.TestCase): - """ - Test the packing methods that work on buffers. - """ - - def test_unpack_from( self ): - test_string = 'abcd01234' - fmt = '4s' - s = struct.Struct(fmt) - for cls in (str, buffer): - data = cls(test_string) - self.assertEquals(s.unpack_from(data), ('abcd',)) - self.assertEquals(s.unpack_from(data, 2), ('cd01',)) - self.assertEquals(s.unpack_from(data, 4), ('0123',)) - for i in xrange(6): - self.assertEquals(s.unpack_from(data, i), (data[i:i+4],)) - for i in xrange(6, len(test_string) + 1): - simple_err(s.unpack_from, data, i) - for cls in (str, buffer): - data = cls(test_string) - self.assertEquals(struct.unpack_from(fmt, data), ('abcd',)) - self.assertEquals(struct.unpack_from(fmt, data, 2), ('cd01',)) - self.assertEquals(struct.unpack_from(fmt, data, 4), ('0123',)) - for i in xrange(6): - self.assertEquals(struct.unpack_from(fmt, data, i), - (data[i:i+4],)) - for i in xrange(6, len(test_string) + 1): - simple_err(struct.unpack_from, fmt, data, i) - - def test_pack_to( self ): - test_string = 'Reykjavik rocks, eow!' - writable_buf = array.array('c', ' '*100) - fmt = '21s' - s = struct.Struct(fmt) - - # Test without offset - s.pack_to(writable_buf, 0, test_string) - from_buf = writable_buf.tostring()[:len(test_string)] - self.assertEquals(from_buf, test_string) - - # Test with offset. - s.pack_to(writable_buf, 10, test_string) - from_buf = writable_buf.tostring()[:len(test_string)+10] - self.assertEquals(from_buf, (test_string[:10] + test_string)) - - # Go beyond boundaries. - small_buf = array.array('c', ' '*10) - self.assertRaises(struct.error, s.pack_to, small_buf, 0, test_string) - self.assertRaises(struct.error, s.pack_to, small_buf, 2, test_string) - - def test_pack_to_fn( self ): - test_string = 'Reykjavik rocks, eow!' - writable_buf = array.array('c', ' '*100) - fmt = '21s' - pack_to = lambda *args: struct.pack_to(fmt, *args) - - # Test without offset - pack_to(writable_buf, 0, test_string) - from_buf = writable_buf.tostring()[:len(test_string)] - self.assertEquals(from_buf, test_string) - - # Test with offset. - pack_to(writable_buf, 10, test_string) - from_buf = writable_buf.tostring()[:len(test_string)+10] - self.assertEquals(from_buf, (test_string[:10] + test_string)) - - # Go beyond boundaries. - small_buf = array.array('c', ' '*10) - self.assertRaises(struct.error, pack_to, small_buf, 0, test_string) - self.assertRaises(struct.error, pack_to, small_buf, 2, test_string) - - -def test_main(): - test.test_support.run_unittest(PackBufferTestCase) - -if __name__ == "__main__": - test_main() + +########################################################################### +# Packing and unpacking to/from buffers. + +# Copied and modified from unittest. +def assertRaises(excClass, callableObj, *args, **kwargs): + try: + callableObj(*args, **kwargs) + except excClass: + return + else: + raise RuntimeError("%s not raised." % excClass) + +def test_unpack_from(): + test_string = 'abcd01234' + fmt = '4s' + s = struct.Struct(fmt) + for cls in (str, buffer): + data = cls(test_string) + assert s.unpack_from(data) == ('abcd',) + assert s.unpack_from(data, 2) == ('cd01',) + assert s.unpack_from(data, 4) == ('0123',) + for i in xrange(6): + assert s.unpack_from(data, i) == (data[i:i+4],) + for i in xrange(6, len(test_string) + 1): + simple_err(s.unpack_from, data, i) + for cls in (str, buffer): + data = cls(test_string) + assert struct.unpack_from(fmt, data) == ('abcd',) + assert struct.unpack_from(fmt, data, 2) == ('cd01',) + assert struct.unpack_from(fmt, data, 4) == ('0123',) + for i in xrange(6): + assert (struct.unpack_from(fmt, data, i) == (data[i:i+4],)) + for i in xrange(6, len(test_string) + 1): + simple_err(struct.unpack_from, fmt, data, i) + +def test_pack_to(): + test_string = 'Reykjavik rocks, eow!' + writable_buf = array.array('c', ' '*100) + fmt = '21s' + s = struct.Struct(fmt) + + # Test without offset + s.pack_to(writable_buf, 0, test_string) + from_buf = writable_buf.tostring()[:len(test_string)] + assert from_buf == test_string + + # Test with offset. + s.pack_to(writable_buf, 10, test_string) + from_buf = writable_buf.tostring()[:len(test_string)+10] + assert from_buf == (test_string[:10] + test_string) + + # Go beyond boundaries. + small_buf = array.array('c', ' '*10) + assertRaises(struct.error, s.pack_to, small_buf, 0, test_string) + assertRaises(struct.error, s.pack_to, small_buf, 2, test_string) + +def test_pack_to_fn(): + test_string = 'Reykjavik rocks, eow!' + writable_buf = array.array('c', ' '*100) + fmt = '21s' + pack_to = lambda *args: struct.pack_to(fmt, *args) + + # Test without offset + pack_to(writable_buf, 0, test_string) + from_buf = writable_buf.tostring()[:len(test_string)] + assert from_buf == test_string + + # Test with offset. + pack_to(writable_buf, 10, test_string) + from_buf = writable_buf.tostring()[:len(test_string)+10] + assert from_buf == (test_string[:10] + test_string) + + # Go beyond boundaries. + small_buf = array.array('c', ' '*10) + assertRaises(struct.error, pack_to, small_buf, 0, test_string) + assertRaises(struct.error, pack_to, small_buf, 2, test_string) + + +# Test methods to pack and unpack from buffers rather than strings. +test_unpack_from() +test_pack_to() +test_pack_to_fn() + diff --git a/Lib/test/test_urllib2.py b/Lib/test/test_urllib2.py index c8f19bc..034b9d0 100644 --- a/Lib/test/test_urllib2.py +++ b/Lib/test/test_urllib2.py @@ -76,10 +76,11 @@ def test_password_manager(self): >>> mgr.find_user_password("c", "http://example.com/bar") ('bar', 'nini') - Currently, we use the highest-level path where more than one match: + Actually, this is really undefined ATM +## Currently, we use the highest-level path where more than one match: - >>> mgr.find_user_password("Some Realm", "http://example.com/ni") - ('joe', 'password') +## >>> mgr.find_user_password("Some Realm", "http://example.com/ni") +## ('joe', 'password') Use latest add_password() in case of conflict: @@ -110,6 +111,53 @@ def test_password_manager(self): pass +def test_password_manager_default_port(self): + """ + >>> mgr = urllib2.HTTPPasswordMgr() + >>> add = mgr.add_password + + The point to note here is that we can't guess the default port if there's + no scheme. This applies to both add_password and find_user_password. + + >>> add("f", "http://g.example.com:80", "10", "j") + >>> add("g", "http://h.example.com", "11", "k") + >>> add("h", "i.example.com:80", "12", "l") + >>> add("i", "j.example.com", "13", "m") + >>> mgr.find_user_password("f", "g.example.com:100") + (None, None) + >>> mgr.find_user_password("f", "g.example.com:80") + ('10', 'j') + >>> mgr.find_user_password("f", "g.example.com") + (None, None) + >>> mgr.find_user_password("f", "http://g.example.com:100") + (None, None) + >>> mgr.find_user_password("f", "http://g.example.com:80") + ('10', 'j') + >>> mgr.find_user_password("f", "http://g.example.com") + ('10', 'j') + >>> mgr.find_user_password("g", "h.example.com") + ('11', 'k') + >>> mgr.find_user_password("g", "h.example.com:80") + ('11', 'k') + >>> mgr.find_user_password("g", "http://h.example.com:80") + ('11', 'k') + >>> mgr.find_user_password("h", "i.example.com") + (None, None) + >>> mgr.find_user_password("h", "i.example.com:80") + ('12', 'l') + >>> mgr.find_user_password("h", "http://i.example.com:80") + ('12', 'l') + >>> mgr.find_user_password("i", "j.example.com") + ('13', 'm') + >>> mgr.find_user_password("i", "j.example.com:80") + (None, None) + >>> mgr.find_user_password("i", "http://j.example.com") + ('13', 'm') + >>> mgr.find_user_password("i", "http://j.example.com:80") + (None, None) + + """ + class MockOpener: addheaders = [] def open(self, req, data=None): @@ -270,6 +318,27 @@ class MockPasswordManager: class OpenerDirectorTests(unittest.TestCase): + def test_badly_named_methods(self): + # test work-around for three methods that accidentally follow the + # naming conventions for handler methods + # (*_open() / *_request() / *_response()) + + # These used to call the accidentally-named methods, causing a + # TypeError in real code; here, returning self from these mock + # methods would either cause no exception, or AttributeError. + + from urllib2 import URLError + + o = OpenerDirector() + meth_spec = [ + [("do_open", "return self"), ("proxy_open", "return self")], + [("redirect_request", "return self")], + ] + handlers = add_ordered_mock_handlers(o, meth_spec) + o.add_handler(urllib2.UnknownHandler()) + for scheme in "do", "proxy", "redirect": + self.assertRaises(URLError, o.open, scheme+"://example.com/") + def test_handled(self): # handler returning non-None means no more handlers will be called o = OpenerDirector() @@ -560,6 +629,7 @@ class HandlerTests(unittest.TestCase): self.method = method self.selector = url self.req_headers += headers.items() + self.req_headers.sort() if body: self.data = body if self.raise_on_endheaders: @@ -758,6 +828,8 @@ class HandlerTests(unittest.TestCase): realm = "ACME Widget Store" http_handler = MockHTTPHandler( 401, 'WWW-Authenticate: Basic realm="%s"\r\n\r\n' % realm) + opener.add_handler(auth_handler) + opener.add_handler(http_handler) self._test_basic_auth(opener, auth_handler, "Authorization", realm, http_handler, password_manager, "http://acme.example.com/protected", @@ -773,6 +845,8 @@ class HandlerTests(unittest.TestCase): realm = "ACME Networks" http_handler = MockHTTPHandler( 407, 'Proxy-Authenticate: Basic realm="%s"\r\n\r\n' % realm) + opener.add_handler(auth_handler) + opener.add_handler(http_handler) self._test_basic_auth(opener, auth_handler, "Proxy-authorization", realm, http_handler, password_manager, "http://acme.example.com:3128/protected", @@ -784,29 +858,53 @@ class HandlerTests(unittest.TestCase): # response (http://python.org/sf/1479302), where it should instead # return None to allow another handler (especially # HTTPBasicAuthHandler) to handle the response. + + # Also (http://python.org/sf/14797027, RFC 2617 section 1.2), we must + # try digest first (since it's the strongest auth scheme), so we record + # order of calls here to check digest comes first: + class RecordingOpenerDirector(OpenerDirector): + def __init__(self): + OpenerDirector.__init__(self) + self.recorded = [] + def record(self, info): + self.recorded.append(info) class TestDigestAuthHandler(urllib2.HTTPDigestAuthHandler): - handler_order = 400 # strictly before HTTPBasicAuthHandler - opener = OpenerDirector() + def http_error_401(self, *args, **kwds): + self.parent.record("digest") + urllib2.HTTPDigestAuthHandler.http_error_401(self, + *args, **kwds) + class TestBasicAuthHandler(urllib2.HTTPBasicAuthHandler): + def http_error_401(self, *args, **kwds): + self.parent.record("basic") + urllib2.HTTPBasicAuthHandler.http_error_401(self, + *args, **kwds) + + opener = RecordingOpenerDirector() password_manager = MockPasswordManager() digest_handler = TestDigestAuthHandler(password_manager) - basic_handler = urllib2.HTTPBasicAuthHandler(password_manager) - opener.add_handler(digest_handler) + basic_handler = TestBasicAuthHandler(password_manager) realm = "ACME Networks" http_handler = MockHTTPHandler( 401, 'WWW-Authenticate: Basic realm="%s"\r\n\r\n' % realm) + opener.add_handler(basic_handler) + opener.add_handler(digest_handler) + opener.add_handler(http_handler) + + # check basic auth isn't blocked by digest handler failing self._test_basic_auth(opener, basic_handler, "Authorization", realm, http_handler, password_manager, "http://acme.example.com/protected", "http://acme.example.com/protected", ) + # check digest was tried before basic (twice, because + # _test_basic_auth called .open() twice) + self.assertEqual(opener.recorded, ["digest", "basic"]*2) def _test_basic_auth(self, opener, auth_handler, auth_header, realm, http_handler, password_manager, request_url, protected_url): import base64, httplib user, password = "wile", "coyote" - opener.add_handler(auth_handler) - opener.add_handler(http_handler) # .add_password() fed through to password manager auth_handler.add_password(realm, request_url, user, password) diff --git a/Lib/test/test_weakref.py b/Lib/test/test_weakref.py index 392e5fa..18ab401 100644 --- a/Lib/test/test_weakref.py +++ b/Lib/test/test_weakref.py @@ -1053,8 +1053,8 @@ libreftest = """ Doctest for examples in the library reference: libweakref.tex ... >>> obj = Dict(red=1, green=2, blue=3) # this object is weak referencable >>> r = weakref.ref(obj) ->>> print r() -{'blue': 3, 'green': 2, 'red': 1} +>>> print r() is obj +True >>> import weakref >>> class Object: diff --git a/Lib/urllib2.py b/Lib/urllib2.py index cdb3a22..227311c 100644 --- a/Lib/urllib2.py +++ b/Lib/urllib2.py @@ -297,6 +297,10 @@ class OpenerDirector: def add_handler(self, handler): added = False for meth in dir(handler): + if meth in ["redirect_request", "do_open", "proxy_open"]: + # oops, coincidental match + continue + i = meth.find("_") protocol = meth[:i] condition = meth[i+1:] @@ -695,32 +699,45 @@ class HTTPPasswordMgr: # uri could be a single URI or a sequence if isinstance(uri, basestring): uri = [uri] - uri = tuple(map(self.reduce_uri, uri)) if not realm in self.passwd: self.passwd[realm] = {} - self.passwd[realm][uri] = (user, passwd) + for default_port in True, False: + reduced_uri = tuple( + [self.reduce_uri(u, default_port) for u in uri]) + self.passwd[realm][reduced_uri] = (user, passwd) def find_user_password(self, realm, authuri): domains = self.passwd.get(realm, {}) - authuri = self.reduce_uri(authuri) - for uris, authinfo in domains.iteritems(): - for uri in uris: - if self.is_suburi(uri, authuri): - return authinfo + for default_port in True, False: + reduced_authuri = self.reduce_uri(authuri, default_port) + for uris, authinfo in domains.iteritems(): + for uri in uris: + if self.is_suburi(uri, reduced_authuri): + return authinfo return None, None - def reduce_uri(self, uri): - """Accept netloc or URI and extract only the netloc and path""" + def reduce_uri(self, uri, default_port=True): + """Accept authority or URI and extract only the authority and path.""" + # note HTTP URLs do not have a userinfo component parts = urlparse.urlsplit(uri) if parts[1]: # URI - return parts[1], parts[2] or '/' - elif parts[0]: - # host:port - return uri, '/' + scheme = parts[0] + authority = parts[1] + path = parts[2] or '/' else: - # host - return parts[2], '/' + # host or host:port + scheme = None + authority = uri + path = '/' + host, port = splitport(authority) + if default_port and port is None and scheme is not None: + dport = {"http": 80, + "https": 443, + }.get(scheme) + if dport is not None: + authority = "%s:%d" % (host, dport) + return authority, path def is_suburi(self, base, test): """Check if test is below base in a URI tree @@ -755,6 +772,10 @@ class AbstractBasicAuthHandler: # www-authenticate header. should probably be a lot more careful # in parsing them to extract multiple alternatives + # XXX could pre-emptively send auth info already accepted (RFC 2617, + # end of section 2, and section 1.2 immediately after "credentials" + # production). + def __init__(self, password_mgr=None): if password_mgr is None: password_mgr = HTTPPasswordMgr() @@ -964,6 +985,7 @@ class HTTPDigestAuthHandler(BaseHandler, AbstractDigestAuthHandler): """ auth_header = 'Authorization' + handler_order = 490 # before Basic auth def http_error_401(self, req, fp, code, msg, headers): host = urlparse.urlparse(req.get_full_url())[1] @@ -976,6 +998,7 @@ class HTTPDigestAuthHandler(BaseHandler, AbstractDigestAuthHandler): class ProxyDigestAuthHandler(BaseHandler, AbstractDigestAuthHandler): auth_header = 'Proxy-Authorization' + handler_order = 490 # before Basic auth def http_error_407(self, req, fp, code, msg, headers): host = req.get_host() |