From python-checkins at python.org Sun Aug 1 00:05:55 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 1 Aug 2010 00:05:55 +0200 (CEST) Subject: [Python-checkins] r83372 - python/branches/py3k/Makefile.pre.in Message-ID: <20100731220555.0AC20EEAAE@mail.python.org> Author: georg.brandl Date: Sun Aug 1 00:05:54 2010 New Revision: 83372 Log: #4007: remove *.a and *.so.X.Y files in "make clean". Modified: python/branches/py3k/Makefile.pre.in Modified: python/branches/py3k/Makefile.pre.in ============================================================================== --- python/branches/py3k/Makefile.pre.in (original) +++ python/branches/py3k/Makefile.pre.in Sun Aug 1 00:05:54 2010 @@ -1185,8 +1185,9 @@ -rm -rf Doc/tools/sphinx Doc/tools/pygments Doc/tools/docutils clean: pycremoval - find . -name '*.o' -exec rm -f {} ';' + find . -name '*.[oa]' -exec rm -f {} ';' find . -name '*.s[ol]' -exec rm -f {} ';' + find . -name '*.so.[0-9]*.[0-9]*' -exec rm -f {} ';' find build -name 'fficonfig.h' -exec rm -f {} ';' || true find build -name 'fficonfig.py' -exec rm -f {} ';' || true -rm -f Lib/lib2to3/*Grammar*.pickle From python-checkins at python.org Sun Aug 1 00:11:11 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 1 Aug 2010 00:11:11 +0200 (CEST) Subject: [Python-checkins] r83373 - in python/branches/py3k: Lib/http/cookiejar.py Misc/NEWS Message-ID: <20100731221111.B6BB3EEAC9@mail.python.org> Author: georg.brandl Date: Sun Aug 1 00:11:11 2010 New Revision: 83373 Log: #5147: revert accidental indentation of header constant for MozillaCookieJar. Modified: python/branches/py3k/Lib/http/cookiejar.py python/branches/py3k/Misc/NEWS Modified: python/branches/py3k/Lib/http/cookiejar.py ============================================================================== --- python/branches/py3k/Lib/http/cookiejar.py (original) +++ python/branches/py3k/Lib/http/cookiejar.py Sun Aug 1 00:11:11 2010 @@ -1974,9 +1974,9 @@ """ magic_re = re.compile("#( Netscape)? HTTP Cookie File") header = """\ - # Netscape HTTP Cookie File - # http://www.netscape.com/newsref/std/cookie_spec.html - # This is a generated file! Do not edit. +# Netscape HTTP Cookie File +# http://www.netscape.com/newsref/std/cookie_spec.html +# This is a generated file! Do not edit. """ Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Sun Aug 1 00:11:11 2010 @@ -15,6 +15,9 @@ Library ------- +- Issue #5147: Fix the header generated for cookie files written by + http.cookiejar.MozillaCookieJar. + - Issue #8198: In pydoc, output all help text to the correct stream when sys.stdout is reassigned. From python-checkins at python.org Sun Aug 1 00:32:52 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 1 Aug 2010 00:32:52 +0200 (CEST) Subject: [Python-checkins] r83374 - in python/branches/py3k: Lib/imaplib.py Misc/NEWS Message-ID: <20100731223252.707D6EE98B@mail.python.org> Author: georg.brandl Date: Sun Aug 1 00:32:52 2010 New Revision: 83374 Log: #5146: handle UID THREAD command correctly. Modified: python/branches/py3k/Lib/imaplib.py python/branches/py3k/Misc/NEWS Modified: python/branches/py3k/Lib/imaplib.py ============================================================================== --- python/branches/py3k/Lib/imaplib.py (original) +++ python/branches/py3k/Lib/imaplib.py Sun Aug 1 00:32:52 2010 @@ -765,7 +765,7 @@ ', '.join(Commands[command]))) name = 'UID' typ, dat = self._simple_command(name, command, *args) - if command in ('SEARCH', 'SORT'): + if command in ('SEARCH', 'SORT', 'THREAD'): name = command else: name = 'FETCH' Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Sun Aug 1 00:32:52 2010 @@ -15,6 +15,8 @@ Library ------- +- Issue #5146: Handle UID THREAD command correctly in imaplib. + - Issue #5147: Fix the header generated for cookie files written by http.cookiejar.MozillaCookieJar. From python-checkins at python.org Sun Aug 1 00:48:02 2010 From: python-checkins at python.org (antoine.pitrou) Date: Sun, 1 Aug 2010 00:48:02 +0200 (CEST) Subject: [Python-checkins] r83375 - python/branches/py3k/Doc/whatsnew/3.2.rst Message-ID: <20100731224802.BDBA9EE994@mail.python.org> Author: antoine.pitrou Date: Sun Aug 1 00:48:02 2010 New Revision: 83375 Log: Reorder entries by module lexicographic order Modified: python/branches/py3k/Doc/whatsnew/3.2.rst Modified: python/branches/py3k/Doc/whatsnew/3.2.rst ============================================================================== --- python/branches/py3k/Doc/whatsnew/3.2.rst (original) +++ python/branches/py3k/Doc/whatsnew/3.2.rst Sun Aug 1 00:48:02 2010 @@ -66,9 +66,41 @@ New, Improved, and Deprecated Modules ===================================== +* The previously deprecated :func:`contextlib.nested` function has been + removed in favor of a plain :keyword:`with` statement which can + accept multiple context managers. The latter technique is faster + (because it is built-in), and it does a better job finalizing multiple + context managers when one of them raises an exception. + + (Contributed by Georg Brandl and Mattias Br?ndstr?m; + `appspot issue 53094 `_.) + * The :class:`ftplib.FTP` class now supports the context manager protocol (Contributed by Tarek Ziad? and Giampaolo Rodol?; :issue:`4972`.) +* The :func:`shutil.copytree` function has two new options: + + * *ignore_dangling_symlinks*: when ``symlinks=False`` (meaning that the + function copies the file pointed to by the symlink, not the symlink + itself) this option will silence the error thrown if the file doesn't + exist. + + * *copy_function*: a callable that will be used to copy files. + :func:`shutil.copy2` is used by default. + + (Contributed by Tarek Ziad?.) + +* The *sqlite3* module has some new features: + + * XXX *enable_load_extension* + + * XXX *load_extension* + + * New :class:`~sqlite3.Connection` attribute + :attr:`~sqlite3.Connection.in_transaction` is :const:`True` when there + are uncommitted changes, and :const:`False` otherwise. (Contributed + by R. David Murray and Shashwat Anand, :issue:`8845`.) + * The :mod:`ssl` module has a new class, :class:`~ssl.SSLContext` which serves as a container for various persistent SSL data, such as protocol settings, certificates, private keys, and various other options. @@ -110,38 +142,6 @@ (Contributed by Georg Brandl; :issue:`5675`.) -* The previously deprecated :func:`contextlib.nested` function has been - removed in favor of a plain :keyword:`with` statement which can - accept multiple context managers. The latter technique is faster - (because it is built-in), and it does a better job finalizing multiple - context managers when one of them raises an exception. - - (Contributed by Georg Brandl and Mattias Br?ndstr?m; - `appspot issue 53094 `_.) - -* The :func:`shutil.copytree` function has two new options: - - * *ignore_dangling_symlinks*: when ``symlinks=False`` (meaning that the - function copies the file pointed to by the symlink, not the symlink - itself) this option will silence the error thrown if the file doesn't - exist. - - * *copy_function*: a callable that will be used to copy files. - :func:`shutil.copy2` is used by default. - - (Contributed by Tarek Ziade.) - -* The *sqlite3* module has some new features: - - * XXX *enable_load_extension* - - * XXX *load_extension* - - * New :class:`~sqlite3.Connection` attribute - :attr:`~sqlite3.Connection.in_transaction` is :const:`True` when there - are uncommitted changes, and :const:`False` otherwise. (Contributed - by R. David Murray and Shashwat Anand, :issue:`8845`.) - Multi-threading =============== From python-checkins at python.org Sun Aug 1 01:33:22 2010 From: python-checkins at python.org (raymond.hettinger) Date: Sun, 1 Aug 2010 01:33:22 +0200 (CEST) Subject: [Python-checkins] r83376 - python/branches/py3k/PC/VS8.0/pythoncore.vcproj Message-ID: <20100731233322.2E2E1EE98B@mail.python.org> Author: raymond.hettinger Date: Sun Aug 1 01:33:22 2010 New Revision: 83376 Log: Fix build on VS8. Modified: python/branches/py3k/PC/VS8.0/pythoncore.vcproj Modified: python/branches/py3k/PC/VS8.0/pythoncore.vcproj ============================================================================== --- python/branches/py3k/PC/VS8.0/pythoncore.vcproj (original) +++ python/branches/py3k/PC/VS8.0/pythoncore.vcproj Sun Aug 1 01:33:22 2010 @@ -1091,7 +1091,7 @@ > Author: r.david.murray Date: Sun Aug 1 03:18:57 2010 New Revision: 83377 Log: Unblocked revisions 75659 via svnmerge ........ r75659 | tarek.ziade | 2009-10-24 09:29:44 -0400 (Sat, 24 Oct 2009) | 1 line #7066 - Fixed distutils.archive_util.make_archive behavior so it restores the cwd ........ Modified: python/branches/release26-maint/ (props changed) From python-checkins at python.org Sun Aug 1 03:33:07 2010 From: python-checkins at python.org (ezio.melotti) Date: Sun, 1 Aug 2010 03:33:07 +0200 (CEST) Subject: [Python-checkins] r83378 - tracker/instances/python-dev/detectors/sendmail.py Message-ID: <20100801013307.8D898EE994@mail.python.org> Author: ezio.melotti Date: Sun Aug 1 03:33:07 2010 New Revision: 83378 Log: Make sure that oldmsglist is initialaized when oldvalues is None. Modified: tracker/instances/python-dev/detectors/sendmail.py Modified: tracker/instances/python-dev/detectors/sendmail.py ============================================================================== --- tracker/instances/python-dev/detectors/sendmail.py (original) +++ tracker/instances/python-dev/detectors/sendmail.py Sun Aug 1 03:33:07 2010 @@ -69,6 +69,7 @@ except KeyError: pass oldfiles = [] + oldmsglist = [] else: changenote = cl.generateChangeNote(nodeid, oldvalues) oldfiles = oldvalues.get('files', []) From python-checkins at python.org Sun Aug 1 03:53:52 2010 From: python-checkins at python.org (r.david.murray) Date: Sun, 1 Aug 2010 03:53:52 +0200 (CEST) Subject: [Python-checkins] r83379 - in python/branches/release26-maint: Lib/distutils/archive_util.py Misc/NEWS Message-ID: <20100801015352.DFF6DEEAB4@mail.python.org> Author: r.david.murray Date: Sun Aug 1 03:53:52 2010 New Revision: 83379 Log: Merged revisions 75659 via svnmerge from svn+ssh://pythondev at svn.python.org/python/trunk Only the try/except was backported; owner and group were added in 2.7, as was the test file. ........ r75659 | tarek.ziade | 2009-10-24 09:29:44 -0400 (Sat, 24 Oct 2009) | 1 line #7066 - Fixed distutils.archive_util.make_archive behavior so it restores the cwd ........ Modified: python/branches/release26-maint/ (props changed) python/branches/release26-maint/Lib/distutils/archive_util.py python/branches/release26-maint/Misc/NEWS Modified: python/branches/release26-maint/Lib/distutils/archive_util.py ============================================================================== --- python/branches/release26-maint/Lib/distutils/archive_util.py (original) +++ python/branches/release26-maint/Lib/distutils/archive_util.py Sun Aug 1 03:53:52 2010 @@ -162,9 +162,12 @@ kwargs[arg] = val filename = apply(func, (base_name, base_dir), kwargs) - if root_dir is not None: - log.debug("changing back to '%s'", save_cwd) - os.chdir(save_cwd) + try: + filename = func(base_name, base_dir, **kwargs) + finally: + if root_dir is not None: + log.debug("changing back to '%s'", save_cwd) + os.chdir(save_cwd) return filename Modified: python/branches/release26-maint/Misc/NEWS ============================================================================== --- python/branches/release26-maint/Misc/NEWS (original) +++ python/branches/release26-maint/Misc/NEWS Sun Aug 1 03:53:52 2010 @@ -84,6 +84,9 @@ Library ------- +- Issue #7066: archive_util.make_archive now restores the cwd if an error is + raised. Initial patch by Ezio Melotti. + - Issue #5006: Better handling of unicode byte-order marks (BOM) in the io library. This means, for example, that opening an UTF-16 text file in append mode doesn't add a BOM at the end of the file if the file isn't empty. From solipsis at pitrou.net Sun Aug 1 05:23:15 2010 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Sun, 01 Aug 2010 05:23:15 +0200 Subject: [Python-checkins] Daily py3k reference leaks (r83376): sum=0 Message-ID: py3k results for svn r83376 (hg cset 2d09c358d008) -------------------------------------------------- Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/py3k/refleaks/reflogbIZaSs', '-x'] From python-checkins at python.org Sun Aug 1 05:31:09 2010 From: python-checkins at python.org (r.david.murray) Date: Sun, 1 Aug 2010 05:31:09 +0200 (CEST) Subject: [Python-checkins] r83380 - in python/branches/py3k: Lib/cmd.py Lib/test/test_cmd.py Misc/ACKS Misc/NEWS Message-ID: <20100801033109.7AD49EEB56@mail.python.org> Author: r.david.murray Date: Sun Aug 1 05:31:09 2010 New Revision: 83380 Log: #8620: Cmd no longer truncates last character if stdin ends without newline Cmd used to blindly chop off the last character of every input line. If the input reached EOF and there was no final new line, it would truncate the last character of the last command. This fix instead strips trailing \r\n from the input lines. While this is a small behavior change, it should not break any working code, since feeding a '\r\n' terminated file to Cmd would previously leave the \r's on the lines, resulting in failed command execution. I wrote the unit test in preparation for a PyOhio TeachMe session run by Catherine Devlin, and we can thank Catherine and the PyOhio session attendees for the fix. I've added Catherine to the Acks file for organizing and leading the TeachMe session, out of which we will hopefully get some new contributors. Modified: python/branches/py3k/Lib/cmd.py python/branches/py3k/Lib/test/test_cmd.py python/branches/py3k/Misc/ACKS python/branches/py3k/Misc/NEWS Modified: python/branches/py3k/Lib/cmd.py ============================================================================== --- python/branches/py3k/Lib/cmd.py (original) +++ python/branches/py3k/Lib/cmd.py Sun Aug 1 05:31:09 2010 @@ -133,7 +133,7 @@ if not len(line): line = 'EOF' else: - line = line[:-1] # chop \n + line = line.rstrip('\r\n') line = self.precmd(line) stop = self.onecmd(line) stop = self.postcmd(stop, line) Modified: python/branches/py3k/Lib/test/test_cmd.py ============================================================================== --- python/branches/py3k/Lib/test/test_cmd.py (original) +++ python/branches/py3k/Lib/test/test_cmd.py Sun Aug 1 05:31:09 2010 @@ -8,7 +8,8 @@ import cmd import sys import re -from io import StringIO +import unittest +import io from test import support class samplecmdclass(cmd.Cmd): @@ -168,9 +169,33 @@ def do_exit(self, arg): return True + +class TestAlternateInput(unittest.TestCase): + + class simplecmd(cmd.Cmd): + + def do_print(self, args): + print(args, file=self.stdout) + + def do_EOF(self, args): + return True + + def test_file_with_missing_final_nl(self): + input = io.StringIO("print test\nprint test2") + output = io.StringIO() + cmd = self.simplecmd(stdin=input, stdout=output) + cmd.use_rawinput = False + cmd.cmdloop() + self.assertMultiLineEqual(output.getvalue(), + ("(Cmd) test\n" + "(Cmd) test2\n" + "(Cmd) ")) + + def test_main(verbose=None): from test import test_cmd support.run_doctest(test_cmd, verbose) + support.run_unittest(TestAlternateInput) def test_coverage(coverdir): trace = support.import_module('trace') Modified: python/branches/py3k/Misc/ACKS ============================================================================== --- python/branches/py3k/Misc/ACKS (original) +++ python/branches/py3k/Misc/ACKS Sun Aug 1 05:31:09 2010 @@ -195,6 +195,7 @@ Arnaud Delobelle Erik Demaine Roger Dev +Catherine Devlin Raghuram Devarakonda Caleb Deveraux Toby Dickenson Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Sun Aug 1 05:31:09 2010 @@ -15,6 +15,9 @@ Library ------- +- Issue #8620: when a Cmd is fed input that reaches EOF without a final + newline, it no longer truncates the last character of the last command line. + - Issue #5146: Handle UID THREAD command correctly in imaplib. - Issue #5147: Fix the header generated for cookie files written by From python-checkins at python.org Sun Aug 1 06:04:06 2010 From: python-checkins at python.org (r.david.murray) Date: Sun, 1 Aug 2010 06:04:06 +0200 (CEST) Subject: [Python-checkins] r83381 - in python/branches/release27-maint: Lib/cmd.py Lib/test/test_cmd.py Misc/ACKS Misc/NEWS Message-ID: <20100801040406.39E6FEEBEF@mail.python.org> Author: r.david.murray Date: Sun Aug 1 06:04:03 2010 New Revision: 83381 Log: Merged revisions 83380 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83380 | r.david.murray | 2010-07-31 23:31:09 -0400 (Sat, 31 Jul 2010) | 17 lines #8620: Cmd no longer truncates last character if stdin ends without newline Cmd used to blindly chop off the last character of every input line. If the input reached EOF and there was no final new line, it would truncate the last character of the last command. This fix instead strips trailing \r\n from the input lines. While this is a small behavior change, it should not break any working code, since feeding a '\r\n' terminated file to Cmd would previously leave the \r's on the lines, resulting in failed command execution. I wrote the unit test in preparation for a PyOhio TeachMe session run by Catherine Devlin, and we can thank Catherine and the PyOhio session attendees for the fix. I've added Catherine to the Acks file for organizing and leading the TeachMe session, out of which we will hopefully get some new contributors. ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Lib/cmd.py python/branches/release27-maint/Lib/test/test_cmd.py python/branches/release27-maint/Misc/ACKS python/branches/release27-maint/Misc/NEWS Modified: python/branches/release27-maint/Lib/cmd.py ============================================================================== --- python/branches/release27-maint/Lib/cmd.py (original) +++ python/branches/release27-maint/Lib/cmd.py Sun Aug 1 06:04:03 2010 @@ -137,7 +137,7 @@ if not len(line): line = 'EOF' else: - line = line[:-1] # chop \n + line = line.rstrip('\r\n') line = self.precmd(line) stop = self.onecmd(line) stop = self.postcmd(stop, line) Modified: python/branches/release27-maint/Lib/test/test_cmd.py ============================================================================== --- python/branches/release27-maint/Lib/test/test_cmd.py (original) +++ python/branches/release27-maint/Lib/test/test_cmd.py Sun Aug 1 06:04:03 2010 @@ -8,6 +8,9 @@ import cmd import sys from test import test_support +import re +import unittest +import StringIO class samplecmdclass(cmd.Cmd): """ @@ -168,9 +171,33 @@ def do_exit(self, arg): return True + +class TestAlternateInput(unittest.TestCase): + + class simplecmd(cmd.Cmd): + + def do_print(self, args): + print >>self.stdout, args + + def do_EOF(self, args): + return True + + def test_file_with_missing_final_nl(self): + input = StringIO.StringIO("print test\nprint test2") + output = StringIO.StringIO() + cmd = self.simplecmd(stdin=input, stdout=output) + cmd.use_rawinput = False + cmd.cmdloop() + self.assertMultiLineEqual(output.getvalue(), + ("(Cmd) test\n" + "(Cmd) test2\n" + "(Cmd) ")) + + def test_main(verbose=None): from test import test_cmd test_support.run_doctest(test_cmd, verbose) + test_support.run_unittest(TestAlternateInput) def test_coverage(coverdir): trace = test_support.import_module('trace') Modified: python/branches/release27-maint/Misc/ACKS ============================================================================== --- python/branches/release27-maint/Misc/ACKS (original) +++ python/branches/release27-maint/Misc/ACKS Sun Aug 1 06:04:03 2010 @@ -188,6 +188,7 @@ Arnaud Delobelle Erik Demaine Roger Dev +Catherine Devlin Raghuram Devarakonda Scott Dial Toby Dickenson Modified: python/branches/release27-maint/Misc/NEWS ============================================================================== --- python/branches/release27-maint/Misc/NEWS (original) +++ python/branches/release27-maint/Misc/NEWS Sun Aug 1 06:04:03 2010 @@ -18,6 +18,9 @@ Library ------- +- Issue #8620: when a Cmd is fed input that reaches EOF without a final + newline, it no longer truncates the last character of the last command line. + - Issue #6213: Implement getstate() and setstate() methods of utf-8-sig and utf-16 incremental encoders. From python-checkins at python.org Sun Aug 1 06:11:05 2010 From: python-checkins at python.org (r.david.murray) Date: Sun, 1 Aug 2010 06:11:05 +0200 (CEST) Subject: [Python-checkins] r83382 - in python/branches/release26-maint: Lib/cmd.py Lib/test/test_cmd.py Misc/ACKS Misc/NEWS Message-ID: <20100801041105.D60A4EEB93@mail.python.org> Author: r.david.murray Date: Sun Aug 1 06:11:05 2010 New Revision: 83382 Log: Merged revisions 83381 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/release27-maint ................ r83381 | r.david.murray | 2010-08-01 00:04:03 -0400 (Sun, 01 Aug 2010) | 23 lines Merged revisions 83380 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83380 | r.david.murray | 2010-07-31 23:31:09 -0400 (Sat, 31 Jul 2010) | 17 lines #8620: Cmd no longer truncates last character if stdin ends without newline Cmd used to blindly chop off the last character of every input line. If the input reached EOF and there was no final new line, it would truncate the last character of the last command. This fix instead strips trailing \r\n from the input lines. While this is a small behavior change, it should not break any working code, since feeding a '\r\n' terminated file to Cmd would previously leave the \r's on the lines, resulting in failed command execution. I wrote the unit test in preparation for a PyOhio TeachMe session run by Catherine Devlin, and we can thank Catherine and the PyOhio session attendees for the fix. I've added Catherine to the Acks file for organizing and leading the TeachMe session, out of which we will hopefully get some new contributors. ........ ................ Modified: python/branches/release26-maint/ (props changed) python/branches/release26-maint/Lib/cmd.py python/branches/release26-maint/Lib/test/test_cmd.py python/branches/release26-maint/Misc/ACKS python/branches/release26-maint/Misc/NEWS Modified: python/branches/release26-maint/Lib/cmd.py ============================================================================== --- python/branches/release26-maint/Lib/cmd.py (original) +++ python/branches/release26-maint/Lib/cmd.py Sun Aug 1 06:11:05 2010 @@ -137,7 +137,7 @@ if not len(line): line = 'EOF' else: - line = line[:-1] # chop \n + line = line.rstrip('\r\n') line = self.precmd(line) stop = self.onecmd(line) stop = self.postcmd(stop, line) Modified: python/branches/release26-maint/Lib/test/test_cmd.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_cmd.py (original) +++ python/branches/release26-maint/Lib/test/test_cmd.py Sun Aug 1 06:11:05 2010 @@ -7,6 +7,9 @@ import cmd import sys +import re +import unittest +import StringIO class samplecmdclass(cmd.Cmd): """ @@ -165,9 +168,33 @@ def do_exit(self, arg): return True + +class TestAlternateInput(unittest.TestCase): + + class simplecmd(cmd.Cmd): + + def do_print(self, args): + print >>self.stdout, args + + def do_EOF(self, args): + return True + + def test_file_with_missing_final_nl(self): + input = StringIO.StringIO("print test\nprint test2") + output = StringIO.StringIO() + cmd = self.simplecmd(stdin=input, stdout=output) + cmd.use_rawinput = False + cmd.cmdloop() + self.assertEqual(output.getvalue(), + ("(Cmd) test\n" + "(Cmd) test2\n" + "(Cmd) ")) + + def test_main(verbose=None): from test import test_support, test_cmd test_support.run_doctest(test_cmd, verbose) + test_support.run_unittest(TestAlternateInput) def test_coverage(coverdir): import trace Modified: python/branches/release26-maint/Misc/ACKS ============================================================================== --- python/branches/release26-maint/Misc/ACKS (original) +++ python/branches/release26-maint/Misc/ACKS Sun Aug 1 06:11:05 2010 @@ -170,6 +170,7 @@ Arnaud Delobelle Erik Demaine Roger Dev +Catherine Devlin Raghuram Devarakonda Scott Dial Toby Dickenson Modified: python/branches/release26-maint/Misc/NEWS ============================================================================== --- python/branches/release26-maint/Misc/NEWS (original) +++ python/branches/release26-maint/Misc/NEWS Sun Aug 1 06:11:05 2010 @@ -84,6 +84,9 @@ Library ------- +- Issue #8620: when a Cmd is fed input that reaches EOF without a final + newline, it no longer truncates the last character of the last command line. + - Issue #7066: archive_util.make_archive now restores the cwd if an error is raised. Initial patch by Ezio Melotti. From python-checkins at python.org Sun Aug 1 06:14:22 2010 From: python-checkins at python.org (r.david.murray) Date: Sun, 1 Aug 2010 06:14:22 +0200 (CEST) Subject: [Python-checkins] r83383 - in python/branches/release31-maint: Lib/cmd.py Lib/test/test_cmd.py Misc/ACKS Misc/NEWS Message-ID: <20100801041422.B4505EEB72@mail.python.org> Author: r.david.murray Date: Sun Aug 1 06:14:22 2010 New Revision: 83383 Log: Merged revisions 83380 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83380 | r.david.murray | 2010-07-31 23:31:09 -0400 (Sat, 31 Jul 2010) | 17 lines #8620: Cmd no longer truncates last character if stdin ends without newline Cmd used to blindly chop off the last character of every input line. If the input reached EOF and there was no final new line, it would truncate the last character of the last command. This fix instead strips trailing \r\n from the input lines. While this is a small behavior change, it should not break any working code, since feeding a '\r\n' terminated file to Cmd would previously leave the \r's on the lines, resulting in failed command execution. I wrote the unit test in preparation for a PyOhio TeachMe session run by Catherine Devlin, and we can thank Catherine and the PyOhio session attendees for the fix. I've added Catherine to the Acks file for organizing and leading the TeachMe session, out of which we will hopefully get some new contributors. ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Lib/cmd.py python/branches/release31-maint/Lib/test/test_cmd.py python/branches/release31-maint/Misc/ACKS python/branches/release31-maint/Misc/NEWS Modified: python/branches/release31-maint/Lib/cmd.py ============================================================================== --- python/branches/release31-maint/Lib/cmd.py (original) +++ python/branches/release31-maint/Lib/cmd.py Sun Aug 1 06:14:22 2010 @@ -134,7 +134,7 @@ if not len(line): line = 'EOF' else: - line = line[:-1] # chop \n + line = line.rstrip('\r\n') line = self.precmd(line) stop = self.onecmd(line) stop = self.postcmd(stop, line) Modified: python/branches/release31-maint/Lib/test/test_cmd.py ============================================================================== --- python/branches/release31-maint/Lib/test/test_cmd.py (original) +++ python/branches/release31-maint/Lib/test/test_cmd.py Sun Aug 1 06:14:22 2010 @@ -9,7 +9,8 @@ import sys import trace import re -from io import StringIO +import unittest +import io class samplecmdclass(cmd.Cmd): """ @@ -166,9 +167,33 @@ def do_exit(self, arg): return True + +class TestAlternateInput(unittest.TestCase): + + class simplecmd(cmd.Cmd): + + def do_print(self, args): + print(args, file=self.stdout) + + def do_EOF(self, args): + return True + + def test_file_with_missing_final_nl(self): + input = io.StringIO("print test\nprint test2") + output = io.StringIO() + cmd = self.simplecmd(stdin=input, stdout=output) + cmd.use_rawinput = False + cmd.cmdloop() + self.assertMultiLineEqual(output.getvalue(), + ("(Cmd) test\n" + "(Cmd) test2\n" + "(Cmd) ")) + + def test_main(verbose=None): from test import support, test_cmd support.run_doctest(test_cmd, verbose) + support.run_unittest(TestAlternateInput) def test_coverage(coverdir): tracer=trace.Trace(ignoredirs=[sys.prefix, sys.exec_prefix,], Modified: python/branches/release31-maint/Misc/ACKS ============================================================================== --- python/branches/release31-maint/Misc/ACKS (original) +++ python/branches/release31-maint/Misc/ACKS Sun Aug 1 06:14:22 2010 @@ -179,6 +179,7 @@ Arnaud Delobelle Erik Demaine Roger Dev +Catherine Devlin Raghuram Devarakonda Toby Dickenson Mark Dickinson Modified: python/branches/release31-maint/Misc/NEWS ============================================================================== --- python/branches/release31-maint/Misc/NEWS (original) +++ python/branches/release31-maint/Misc/NEWS Sun Aug 1 06:14:22 2010 @@ -78,6 +78,9 @@ Library ------- +- Issue #8620: when a Cmd is fed input that reaches EOF without a final + newline, it no longer truncates the last character of the last command line. + - Issue #3704: http.cookiejar was not properly handling URLs with a / in the parameters. From python-checkins at python.org Sun Aug 1 08:32:55 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 1 Aug 2010 08:32:55 +0200 (CEST) Subject: [Python-checkins] r83384 - python/branches/py3k/Lib/pydoc.py Message-ID: <20100801063255.8FA67EE993@mail.python.org> Author: georg.brandl Date: Sun Aug 1 08:32:55 2010 New Revision: 83384 Log: Build properties using lambdas. This makes test_pyclbr pass again, because it does not think that input and output are methods anymore. Modified: python/branches/py3k/Lib/pydoc.py Modified: python/branches/py3k/Lib/pydoc.py ============================================================================== --- python/branches/py3k/Lib/pydoc.py (original) +++ python/branches/py3k/Lib/pydoc.py Sun Aug 1 08:32:55 2010 @@ -1699,13 +1699,8 @@ self._input = input self._output = output - @property - def input(self): - return self._input or sys.stdin - - @property - def output(self): - return self._output or sys.stdout + input = property(lambda self: self._input or sys.stdin) + output = property(lambda self: self._output or sys.stdout) def __repr__(self): if inspect.stack()[1][3] == '?': From python-checkins at python.org Sun Aug 1 08:42:45 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 1 Aug 2010 08:42:45 +0200 (CEST) Subject: [Python-checkins] r83385 - python/branches/py3k/Lib/mailbox.py Message-ID: <20100801064245.B5666EE993@mail.python.org> Author: georg.brandl Date: Sun Aug 1 08:42:45 2010 New Revision: 83385 Log: #8773: mailbox.py does not need to be executable. Modified: python/branches/py3k/Lib/mailbox.py (props changed) From python-checkins at python.org Sun Aug 1 08:44:46 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 1 Aug 2010 08:44:46 +0200 (CEST) Subject: [Python-checkins] r83386 - python/branches/py3k/Lib/test/test_set.py Message-ID: <20100801064446.30C14EE993@mail.python.org> Author: georg.brandl Date: Sun Aug 1 08:44:46 2010 New Revision: 83386 Log: #8768: name test method properly so that it gets executed. Modified: python/branches/py3k/Lib/test/test_set.py Modified: python/branches/py3k/Lib/test/test_set.py ============================================================================== --- python/branches/py3k/Lib/test/test_set.py (original) +++ python/branches/py3k/Lib/test/test_set.py Sun Aug 1 08:44:46 2010 @@ -796,7 +796,7 @@ result = self.set ^ self.set self.assertEqual(result, empty_set) - def checkempty_symmetric_difference(self): + def test_empty_symmetric_difference(self): result = self.set ^ empty_set self.assertEqual(result, self.set) From python-checkins at python.org Sun Aug 1 08:53:28 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 1 Aug 2010 08:53:28 +0200 (CEST) Subject: [Python-checkins] r83387 - python/branches/py3k/Doc/library/optparse.rst Message-ID: <20100801065328.3A9B4EE994@mail.python.org> Author: georg.brandl Date: Sun Aug 1 08:53:28 2010 New Revision: 83387 Log: #8735: better explain semantics of *values* argument for parse(). Modified: python/branches/py3k/Doc/library/optparse.rst Modified: python/branches/py3k/Doc/library/optparse.rst ============================================================================== --- python/branches/py3k/Doc/library/optparse.rst (original) +++ python/branches/py3k/Doc/library/optparse.rst Sun Aug 1 08:53:28 2010 @@ -1234,8 +1234,9 @@ the list of arguments to process (default: ``sys.argv[1:]``) ``values`` - object to store option arguments in (default: a new instance of - :class:`optparse.Values`) + a :class:`optparse.Values` object to store option arguments in (default: a + new instance of :class:`Values`) -- if you give an existing object, the + option defaults will not be initialized on it and the return values are From python-checkins at python.org Sun Aug 1 09:48:44 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 1 Aug 2010 09:48:44 +0200 (CEST) Subject: [Python-checkins] r83388 - python/branches/py3k/Lib/pstats.py Message-ID: <20100801074844.217D6EEBF6@mail.python.org> Author: georg.brandl Date: Sun Aug 1 09:48:43 2010 New Revision: 83388 Log: #7395: fix traceback in do_add() when no stats are loaded. Apply same fix for do_sort() and do_reverse(). Modified: python/branches/py3k/Lib/pstats.py Modified: python/branches/py3k/Lib/pstats.py ============================================================================== --- python/branches/py3k/Lib/pstats.py (original) +++ python/branches/py3k/Lib/pstats.py Sun Aug 1 09:48:43 2010 @@ -576,7 +576,10 @@ print(" that match it are printed.", file=self.stream) def do_add(self, line): - self.stats.add(line) + if self.stats: + self.stats.add(line) + else: + print("No statistics object is loaded.", file=self.stream) return 0 def help_add(self): print("Add profile info from given file to current statistics object.", file=self.stream) @@ -621,12 +624,18 @@ print("Read in profile data from a specified file.", file=self.stream) def do_reverse(self, line): - self.stats.reverse_order() + if self.stats: + self.stats.reverse_order() + else: + print("No statistics object is loaded.", file=self.stream) return 0 def help_reverse(self): print("Reverse the sort order of the profiling report.", file=self.stream) def do_sort(self, line): + if not self.stats: + print("No statistics object is loaded.", file=self.stream) + return abbrevs = self.stats.get_sort_arg_defs() if line and all((x in abbrevs) for x in line.split()): self.stats.sort_stats(*line.split()) @@ -648,8 +657,10 @@ self.generic_help() def do_strip(self, line): - self.stats.strip_dirs() - return 0 + if self.stats: + self.stats.strip_dirs() + else: + print("No statistics object is loaded.", file=self.stream) def help_strip(self): print("Strip leading path information from filenames in the report.", file=self.stream) From python-checkins at python.org Sun Aug 1 09:57:47 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 1 Aug 2010 09:57:47 +0200 (CEST) Subject: [Python-checkins] r83389 - python/branches/py3k/Lib/pstats.py Message-ID: <20100801075747.B6078EE994@mail.python.org> Author: georg.brandl Date: Sun Aug 1 09:57:47 2010 New Revision: 83389 Log: Small improvements to pstats browser: do not crash on reading invalid file, and actually do a reload when executing "read" as intended. Modified: python/branches/py3k/Lib/pstats.py Modified: python/branches/py3k/Lib/pstats.py ============================================================================== --- python/branches/py3k/Lib/pstats.py (original) +++ python/branches/py3k/Lib/pstats.py Sun Aug 1 09:57:47 2010 @@ -614,14 +614,19 @@ except IOError as err: print(err.args[1], file=self.stream) return + except Exception as err: + print(err.__class__.__name__ + ':', err, file=self.stream) + return self.prompt = line + "% " elif len(self.prompt) > 2: - line = self.prompt[-2:] + line = self.prompt[:-2] + self.do_read(line) else: print("No statistics object is current -- cannot reload.", file=self.stream) return 0 def help_read(self): print("Read in profile data from a specified file.", file=self.stream) + print("Without argument, reload the current file.", file=self.stream) def do_reverse(self, line): if self.stats: @@ -664,6 +669,9 @@ def help_strip(self): print("Strip leading path information from filenames in the report.", file=self.stream) + def help_help(self): + print("Show help for a given command.", file=self.stream) + def postcmd(self, stop, line): if stop: return stop From python-checkins at python.org Sun Aug 1 10:07:49 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 1 Aug 2010 10:07:49 +0200 (CEST) Subject: [Python-checkins] r83390 - in python/branches/py3k: Lib/test/sortperf.py Misc/NEWS Message-ID: <20100801080749.60C38EEBB7@mail.python.org> Author: georg.brandl Date: Sun Aug 1 10:07:49 2010 New Revision: 83390 Log: #8230: make Lib/test/sortperf.py run on Python 3. Modified: python/branches/py3k/Lib/test/sortperf.py python/branches/py3k/Misc/NEWS Modified: python/branches/py3k/Lib/test/sortperf.py ============================================================================== --- python/branches/py3k/Lib/test/sortperf.py (original) +++ python/branches/py3k/Lib/test/sortperf.py Sun Aug 1 10:07:49 2010 @@ -118,12 +118,12 @@ L = L * (n // 4) # Force the elements to be distinct objects, else timings can be # artificially low. - L = map(lambda x: --x, L) + L = list(map(lambda x: --x, L)) doit(L) # ~sort del L # All equal. Again, force the elements to be distinct objects. - L = map(abs, [-0.5] * n) + L = list(map(abs, [-0.5] * n)) doit(L) # =sort del L @@ -131,11 +131,11 @@ # for an older implementation of quicksort, which used the median # of the first, last and middle elements as the pivot. half = n // 2 - L = range(half - 1, -1, -1) + L = list(range(half - 1, -1, -1)) L.extend(range(half)) # Force to float, so that the timings are comparable. This is # significantly faster if we leave tham as ints. - L = map(float, L) + L = list(map(float, L)) doit(L) # !sort print() Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Sun Aug 1 10:07:49 2010 @@ -15,6 +15,8 @@ Library ------- +- Issue #8230: Fix Lib/test/sortperf.py. + - Issue #8620: when a Cmd is fed input that reaches EOF without a final newline, it no longer truncates the last character of the last command line. From python-checkins at python.org Sun Aug 1 10:10:09 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 1 Aug 2010 10:10:09 +0200 (CEST) Subject: [Python-checkins] r83391 - python/branches/py3k/Misc/NEWS Message-ID: <20100801081009.15631EE993@mail.python.org> Author: georg.brandl Date: Sun Aug 1 10:10:08 2010 New Revision: 83391 Log: Add another news entry. Modified: python/branches/py3k/Misc/NEWS Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Sun Aug 1 10:10:08 2010 @@ -15,6 +15,8 @@ Library ------- +- Issue #7395: Fix tracebacks in pstats interactive browser. + - Issue #8230: Fix Lib/test/sortperf.py. - Issue #8620: when a Cmd is fed input that reaches EOF without a final From python-checkins at python.org Sun Aug 1 10:22:05 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 1 Aug 2010 10:22:05 +0200 (CEST) Subject: [Python-checkins] r83392 - in python/branches/release27-maint: Lib/doctest.py Lib/test/test_doctest.py Misc/NEWS Message-ID: <20100801082205.45A22EECB0@mail.python.org> Author: georg.brandl Date: Sun Aug 1 10:22:05 2010 New Revision: 83392 Log: #8471: reset _SpoofOut.buf to an empty string when truncating; if Unicode had been output previously, it had been coerced to a Unicode string, potentially making subsequent prints behave differently or raise UnicodeErrors. Modified: python/branches/release27-maint/Lib/doctest.py python/branches/release27-maint/Lib/test/test_doctest.py python/branches/release27-maint/Misc/NEWS Modified: python/branches/release27-maint/Lib/doctest.py ============================================================================== --- python/branches/release27-maint/Lib/doctest.py (original) +++ python/branches/release27-maint/Lib/doctest.py Sun Aug 1 10:22:05 2010 @@ -263,6 +263,9 @@ StringIO.truncate(self, size) if hasattr(self, "softspace"): del self.softspace + if not self.buf: + # Reset it to an empty string, to make sure it's not unicode. + self.buf = '' # Worst-case linear-time ellipsis matching. def _ellipsis_match(want, got): Modified: python/branches/release27-maint/Lib/test/test_doctest.py ============================================================================== --- python/branches/release27-maint/Lib/test/test_doctest.py (original) +++ python/branches/release27-maint/Lib/test/test_doctest.py Sun Aug 1 10:22:05 2010 @@ -1581,7 +1581,33 @@ >>> test = doctest.DocTestParser().get_doctest(s, {}, 's', 's.py', 0) Traceback (most recent call last): ValueError: line 0 of the doctest for s has an option directive on a line with no example: '# doctest: +ELLIPSIS' -""" + + """ + + def test_unicode_output(self): r""" + +Check that unicode output works: + + >>> u'\xe9' + u'\xe9' + +If we return unicode, SpoofOut's buf variable becomes automagically +converted to unicode. This means all subsequent output becomes converted +to unicode, and if the output contains non-ascii characters that failed. +It used to be that this state change carried on between tests, meaning +tests would fail if unicode has been output previously in the testrun. +This test tests that this is no longer so: + + >>> print u'abc' + abc + +And then return a string with non-ascii characters: + + >>> print u'\xe9'.encode('utf-8') + ? + + """ + def test_testsource(): r""" Unit tests for `testsource()`. Modified: python/branches/release27-maint/Misc/NEWS ============================================================================== --- python/branches/release27-maint/Misc/NEWS (original) +++ python/branches/release27-maint/Misc/NEWS Sun Aug 1 10:22:05 2010 @@ -18,6 +18,9 @@ Library ------- +- Issue #8471: In doctest, properly reset the output stream to an empty + string when Unicode was previously output. + - Issue #8620: when a Cmd is fed input that reaches EOF without a final newline, it no longer truncates the last character of the last command line. From python-checkins at python.org Sun Aug 1 10:35:29 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 1 Aug 2010 10:35:29 +0200 (CEST) Subject: [Python-checkins] r83393 - in python/branches/py3k: Lib/trace.py Misc/NEWS Message-ID: <20100801083529.683AAEE993@mail.python.org> Author: georg.brandl Date: Sun Aug 1 10:35:29 2010 New Revision: 83393 Log: #1690103: fix initial namespace for code run with trace.main(). Modified: python/branches/py3k/Lib/trace.py python/branches/py3k/Misc/NEWS Modified: python/branches/py3k/Lib/trace.py ============================================================================== --- python/branches/py3k/Lib/trace.py (original) +++ python/branches/py3k/Lib/trace.py Sun Aug 1 10:35:29 2010 @@ -799,7 +799,14 @@ try: with open(progname) as fp: code = compile(fp.read(), progname, 'exec') - t.run(code) + # try to emulate __main__ namespace as much as possible + globs = { + '__file__': progname, + '__name__': '__main__', + '__package__': None, + '__cached__': None, + } + t.runctx(code, globs, globs) except IOError as err: _err_exit("Cannot run file %r because: %s" % (sys.argv[0], err)) except SystemExit: Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Sun Aug 1 10:35:29 2010 @@ -15,6 +15,8 @@ Library ------- +- Issue #1690103: Fix initial namespace for code run with trace.main(). + - Issue #7395: Fix tracebacks in pstats interactive browser. - Issue #8230: Fix Lib/test/sortperf.py. From python-checkins at python.org Sun Aug 1 10:46:24 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 1 Aug 2010 10:46:24 +0200 (CEST) Subject: [Python-checkins] r83394 - python/branches/py3k/Makefile.pre.in Message-ID: <20100801084624.C6486EE993@mail.python.org> Author: georg.brandl Date: Sun Aug 1 10:46:24 2010 New Revision: 83394 Log: No need to split this, there are enough long lines. Modified: python/branches/py3k/Makefile.pre.in Modified: python/branches/py3k/Makefile.pre.in ============================================================================== --- python/branches/py3k/Makefile.pre.in (original) +++ python/branches/py3k/Makefile.pre.in Sun Aug 1 10:46:24 2010 @@ -417,9 +417,7 @@ # Build the interpreter $(BUILDPYTHON): Modules/python.o $(LIBRARY) $(LDLIBRARY) - $(LINKCC) $(PY_LDFLAGS) $(LINKFORSHARED) -o $@ \ - Modules/python.o \ - $(BLDLIBRARY) $(LIBS) $(MODLIBS) $(SYSLIBS) $(LDLAST) + $(LINKCC) $(PY_LDFLAGS) $(LINKFORSHARED) -o $@ Modules/python.o $(BLDLIBRARY) $(LIBS) $(MODLIBS) $(SYSLIBS) $(LDLAST) platform: $(BUILDPYTHON) $(RUNSHARED) ./$(BUILDPYTHON) -E -c 'import sys ; from sysconfig import get_platform ; print(get_platform()+"-"+sys.version[0:3])' >platform From python-checkins at python.org Sun Aug 1 10:49:18 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 1 Aug 2010 10:49:18 +0200 (CEST) Subject: [Python-checkins] r83395 - python/branches/py3k/Objects/unicodeobject.c Message-ID: <20100801084918.CDAB5EE993@mail.python.org> Author: georg.brandl Date: Sun Aug 1 10:49:18 2010 New Revision: 83395 Log: #8821: do not rely on Unicode strings being terminated with a \u0000, rather explicitly check range before looking for a second surrogate character. Modified: python/branches/py3k/Objects/unicodeobject.c Modified: python/branches/py3k/Objects/unicodeobject.c ============================================================================== --- python/branches/py3k/Objects/unicodeobject.c (original) +++ python/branches/py3k/Objects/unicodeobject.c Sun Aug 1 10:49:18 2010 @@ -3734,7 +3734,7 @@ ch2 = *s++; size--; - if (ch2 >= 0xDC00 && ch2 <= 0xDFFF) { + if (ch2 >= 0xDC00 && ch2 <= 0xDFFF && size) { ucs = (((ch & 0x03FF) << 10) | (ch2 & 0x03FF)) + 0x00010000; *p++ = '\\'; *p++ = 'U'; @@ -3976,7 +3976,7 @@ ch2 = *s++; size--; - if (ch2 >= 0xDC00 && ch2 <= 0xDFFF) { + if (ch2 >= 0xDC00 && ch2 <= 0xDFFF && size) { ucs = (((ch & 0x03FF) << 10) | (ch2 & 0x03FF)) + 0x00010000; *p++ = '\\'; *p++ = 'U'; From python-checkins at python.org Sun Aug 1 10:52:32 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 1 Aug 2010 10:52:32 +0200 (CEST) Subject: [Python-checkins] r83396 - python/branches/py3k/Lib/timeit.py Message-ID: <20100801085232.6B946EE993@mail.python.org> Author: georg.brandl Date: Sun Aug 1 10:52:32 2010 New Revision: 83396 Log: #4810: document "--" option separator in timeit help. Modified: python/branches/py3k/Lib/timeit.py Modified: python/branches/py3k/Lib/timeit.py ============================================================================== --- python/branches/py3k/Lib/timeit.py (original) +++ python/branches/py3k/Lib/timeit.py Sun Aug 1 10:52:32 2010 @@ -9,7 +9,7 @@ Library usage: see the Timer class. Command line usage: - python timeit.py [-n N] [-r N] [-s S] [-t] [-c] [-h] [statement] + python timeit.py [-n N] [-r N] [-s S] [-t] [-c] [-h] [--] [statement] Options: -n/--number N: how many times to execute 'statement' (default: see below) @@ -19,6 +19,7 @@ -c/--clock: use time.clock() (default on Windows) -v/--verbose: print raw timing results; repeat for more digits precision -h/--help: print this usage message and exit + --: separate options from statement, use when statement starts with - statement: statement to be timed (default 'pass') A multi-line statement may be given by specifying each line as a From python-checkins at python.org Sun Aug 1 11:02:50 2010 From: python-checkins at python.org (ronald.oussoren) Date: Sun, 1 Aug 2010 11:02:50 +0200 (CEST) Subject: [Python-checkins] r83397 - in python/branches/py3k/Lib: site.py sysconfig.py test/test_site.py Message-ID: <20100801090250.8E0A4EE984@mail.python.org> Author: ronald.oussoren Date: Sun Aug 1 11:02:50 2010 New Revision: 83397 Log: Ensure that test_site actually passes with a framework build Modified: python/branches/py3k/Lib/site.py python/branches/py3k/Lib/sysconfig.py python/branches/py3k/Lib/test/test_site.py Modified: python/branches/py3k/Lib/site.py ============================================================================== --- python/branches/py3k/Lib/site.py (original) +++ python/branches/py3k/Lib/site.py Sun Aug 1 11:02:50 2010 @@ -295,7 +295,7 @@ # locations. from sysconfig import get_config_var framework = get_config_var("PYTHONFRAMEWORK") - if framework and "/%s.framework/"%(framework,) in prefix: + if framework: sitepackages.append( os.path.join("/Library", framework, sys.version[:3], "site-packages")) Modified: python/branches/py3k/Lib/sysconfig.py ============================================================================== --- python/branches/py3k/Lib/sysconfig.py (original) +++ python/branches/py3k/Lib/sysconfig.py Sun Aug 1 11:02:50 2010 @@ -173,7 +173,7 @@ if sys.platform == "darwin": framework = get_config_var("PYTHONFRAMEWORK") if framework: - return joinuser("~", "Library", framework, "%d.%d"%( + return env_base if env_base else joinuser("~", "Library", framework, "%d.%d"%( sys.version_info[:2])) return env_base if env_base else joinuser("~", ".local") Modified: python/branches/py3k/Lib/test/test_site.py ============================================================================== --- python/branches/py3k/Lib/test/test_site.py (original) +++ python/branches/py3k/Lib/test/test_site.py Sun Aug 1 11:02:50 2010 @@ -185,13 +185,10 @@ sysconfig.get_config_var("PYTHONFRAMEWORK")): site.PREFIXES = ['Python.framework'] dirs = site.getsitepackages() - self.assertEqual(len(dirs), 4) - wanted = os.path.join('~', 'Library', 'Python', - sys.version[:3], 'site-packages') - self.assertEquals(dirs[2], os.path.expanduser(wanted)) + self.assertEqual(len(dirs), 3) wanted = os.path.join('/Library', 'Python', sys.version[:3], 'site-packages') - self.assertEquals(dirs[3], wanted) + self.assertEquals(dirs[2], wanted) class PthFile(object): """Helper class for handling testing of .pth files""" From python-checkins at python.org Sun Aug 1 11:06:34 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 1 Aug 2010 11:06:34 +0200 (CEST) Subject: [Python-checkins] r83398 - in python/branches/py3k: Lib/http/cookies.py Lib/test/test_http_cookies.py Misc/NEWS Message-ID: <20100801090634.ABABAEE984@mail.python.org> Author: georg.brandl Date: Sun Aug 1 11:06:34 2010 New Revision: 83398 Log: #8826: the "expires" attribute value is a date string with spaces, but apparently not all user-agents put it in quotes. Handle that as a special case. Modified: python/branches/py3k/Lib/http/cookies.py python/branches/py3k/Lib/test/test_http_cookies.py python/branches/py3k/Misc/NEWS Modified: python/branches/py3k/Lib/http/cookies.py ============================================================================== --- python/branches/py3k/Lib/http/cookies.py (original) +++ python/branches/py3k/Lib/http/cookies.py Sun Aug 1 11:06:34 2010 @@ -434,6 +434,8 @@ (?P # Start of group 'val' "(?:[^\\"]|\\.)*" # Any doublequoted string | # or + \w{3},\s[\w\d-]{9,11}\s[\d:]{8}\sGMT # Special case for "expires" attr + | # or """ + _LegalCharsPatt + r"""* # Any word or empty string ) # End of group 'val' \s*;? # Probably ending in a semi-colon Modified: python/branches/py3k/Lib/test/test_http_cookies.py ============================================================================== --- python/branches/py3k/Lib/test/test_http_cookies.py (original) +++ python/branches/py3k/Lib/test/test_http_cookies.py Sun Aug 1 11:06:34 2010 @@ -76,6 +76,16 @@ # can't test exact output, it always depends on current date/time self.assertTrue(C.output().endswith('GMT')) + # loading 'expires' + C = cookies.SimpleCookie() + C.load('Customer="W"; expires=Wed, 01-Jan-2010 00:00:00 GMT') + self.assertEqual(C['Customer']['expires'], + 'Wed, 01-Jan-2010 00:00:00 GMT') + C = cookies.SimpleCookie() + C.load('Customer="W"; expires=Wed, 01-Jan-98 00:00:00 GMT') + self.assertEqual(C['Customer']['expires'], + 'Wed, 01-Jan-98 00:00:00 GMT') + # 'max-age' C = cookies.SimpleCookie('Customer="WILE_E_COYOTE"') C['Customer']['max-age'] = 10 Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Sun Aug 1 11:06:34 2010 @@ -15,6 +15,8 @@ Library ------- +- Issue #8826: Properly load old-style "expires" attribute in http.cookies. + - Issue #1690103: Fix initial namespace for code run with trace.main(). - Issue #7395: Fix tracebacks in pstats interactive browser. From python-checkins at python.org Sun Aug 1 11:17:53 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 1 Aug 2010 11:17:53 +0200 (CEST) Subject: [Python-checkins] r83399 - python/branches/py3k/Tools/msi/msi.py Message-ID: <20100801091753.7976BEE984@mail.python.org> Author: georg.brandl Date: Sun Aug 1 11:17:53 2010 New Revision: 83399 Log: Package some new files that are needed for running the test suite from the MSI package. Modified: python/branches/py3k/Tools/msi/msi.py Modified: python/branches/py3k/Tools/msi/msi.py ============================================================================== --- python/branches/py3k/Tools/msi/msi.py (original) +++ python/branches/py3k/Tools/msi/msi.py Sun Aug 1 11:17:53 2010 @@ -1009,6 +1009,8 @@ lib.add_file("185test.db") lib.add_file("audiotest.au") lib.add_file("cfgparser.1") + lib.add_file("cfgparser.2") + lib.add_file("cfgparser.3") lib.add_file("sgml_input.html") lib.add_file("testtar.tar") lib.add_file("test_difflib_expect.html") @@ -1019,6 +1021,8 @@ lib.glob("*.pem") lib.glob("*.pck") lib.add_file("zipdir.zip") + if dir=='tests' and parent.physical=='distutils': + lib.add_file("Setup.sample") if dir=='decimaltestdata': lib.glob("*.decTest") if dir=='xmltestdata': From python-checkins at python.org Sun Aug 1 12:41:49 2010 From: python-checkins at python.org (mark.dickinson) Date: Sun, 1 Aug 2010 12:41:49 +0200 (CEST) Subject: [Python-checkins] r83400 - in python/branches/py3k: Lib/test/test_complex.py Misc/NEWS Objects/stringlib/formatter.h Message-ID: <20100801104149.39CFCEEE49@mail.python.org> Author: mark.dickinson Date: Sun Aug 1 12:41:49 2010 New Revision: 83400 Log: Issue #9416: Fix some issues with complex formatting where the output with no type specifier failed to match the str output: - format(complex(-0.0, 2.0), '-') omitted the real part from the output, - format(complex(0.0, 2.0), '-') included a sign and parentheses. Modified: python/branches/py3k/Lib/test/test_complex.py python/branches/py3k/Misc/NEWS python/branches/py3k/Objects/stringlib/formatter.h Modified: python/branches/py3k/Lib/test/test_complex.py ============================================================================== --- python/branches/py3k/Lib/test/test_complex.py (original) +++ python/branches/py3k/Lib/test/test_complex.py Sun Aug 1 12:41:49 2010 @@ -506,6 +506,16 @@ self.assertEqual(format(z, '-'), str(z)) self.assertEqual(format(z, '<'), str(z)) self.assertEqual(format(z, '10'), str(z)) + z = complex(0.0, 3.0) + self.assertEqual(format(z, ''), str(z)) + self.assertEqual(format(z, '-'), str(z)) + self.assertEqual(format(z, '<'), str(z)) + self.assertEqual(format(z, '2'), str(z)) + z = complex(-0.0, 2.0) + self.assertEqual(format(z, ''), str(z)) + self.assertEqual(format(z, '-'), str(z)) + self.assertEqual(format(z, '<'), str(z)) + self.assertEqual(format(z, '3'), str(z)) self.assertEqual(format(1+3j, 'g'), '1+3j') self.assertEqual(format(3j, 'g'), '0+3j') Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Sun Aug 1 12:41:49 2010 @@ -12,6 +12,12 @@ Core and Builtins ----------------- +- Issue #9416: Fix some issues with complex formatting where the + output with no type specifier failed to match the str output: + + - format(complex(-0.0, 2.0), '-') omitted the real part from the output, + - format(complex(0.0, 2.0), '-') included a sign and parentheses. + Library ------- Modified: python/branches/py3k/Objects/stringlib/formatter.h ============================================================================== --- python/branches/py3k/Objects/stringlib/formatter.h (original) +++ python/branches/py3k/Objects/stringlib/formatter.h Sun Aug 1 12:41:49 2010 @@ -1136,9 +1136,10 @@ /* Omitted type specifier. Should be like str(self). */ type = 'g'; default_precision = PyFloat_STR_PRECISION; - add_parens = 1; - if (re == 0.0) + if (re == 0.0 && copysign(1.0, re) == 1.0) skip_re = 1; + else + add_parens = 1; } if (type == 'n') @@ -1223,8 +1224,11 @@ n_re_digits, n_re_remainder, re_has_decimal, &locale, &tmp_format); - /* Same formatting, but always include a sign. */ - tmp_format.sign = '+'; + /* Same formatting, but always include a sign, unless the real part is + * going to be omitted, in which case we use whatever sign convention was + * requested by the original format. */ + if (!skip_re) + tmp_format.sign = '+'; n_im_total = calc_number_widths(&im_spec, 0, im_sign_char, p_im, n_im_digits, n_im_remainder, im_has_decimal, &locale, &tmp_format); From python-checkins at python.org Sun Aug 1 12:43:42 2010 From: python-checkins at python.org (mark.dickinson) Date: Sun, 1 Aug 2010 12:43:42 +0200 (CEST) Subject: [Python-checkins] r83401 - in python/branches/release31-maint: Lib/test/test_complex.py Misc/NEWS Objects/stringlib/formatter.h Message-ID: <20100801104342.AD610EE984@mail.python.org> Author: mark.dickinson Date: Sun Aug 1 12:43:42 2010 New Revision: 83401 Log: Merged revisions 83400 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83400 | mark.dickinson | 2010-08-01 11:41:49 +0100 (Sun, 01 Aug 2010) | 7 lines Issue #9416: Fix some issues with complex formatting where the output with no type specifier failed to match the str output: - format(complex(-0.0, 2.0), '-') omitted the real part from the output, - format(complex(0.0, 2.0), '-') included a sign and parentheses. ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Lib/test/test_complex.py python/branches/release31-maint/Misc/NEWS python/branches/release31-maint/Objects/stringlib/formatter.h Modified: python/branches/release31-maint/Lib/test/test_complex.py ============================================================================== --- python/branches/release31-maint/Lib/test/test_complex.py (original) +++ python/branches/release31-maint/Lib/test/test_complex.py Sun Aug 1 12:43:42 2010 @@ -480,6 +480,16 @@ self.assertEqual(format(z, '-'), str(z)) self.assertEqual(format(z, '<'), str(z)) self.assertEqual(format(z, '10'), str(z)) + z = complex(0.0, 3.0) + self.assertEqual(format(z, ''), str(z)) + self.assertEqual(format(z, '-'), str(z)) + self.assertEqual(format(z, '<'), str(z)) + self.assertEqual(format(z, '2'), str(z)) + z = complex(-0.0, 2.0) + self.assertEqual(format(z, ''), str(z)) + self.assertEqual(format(z, '-'), str(z)) + self.assertEqual(format(z, '<'), str(z)) + self.assertEqual(format(z, '3'), str(z)) self.assertEqual(format(1+3j, 'g'), '1+3j') self.assertEqual(format(3j, 'g'), '0+3j') Modified: python/branches/release31-maint/Misc/NEWS ============================================================================== --- python/branches/release31-maint/Misc/NEWS (original) +++ python/branches/release31-maint/Misc/NEWS Sun Aug 1 12:43:42 2010 @@ -12,6 +12,12 @@ Core and Builtins ----------------- +- Issue #9416: Fix some issues with complex formatting where the + output with no type specifier failed to match the str output: + + - format(complex(-0.0, 2.0), '-') omitted the real part from the output, + - format(complex(0.0, 2.0), '-') included a sign and parentheses. + - Issue #7616: Fix copying of overlapping memoryview slices with the Intel compiler. Modified: python/branches/release31-maint/Objects/stringlib/formatter.h ============================================================================== --- python/branches/release31-maint/Objects/stringlib/formatter.h (original) +++ python/branches/release31-maint/Objects/stringlib/formatter.h Sun Aug 1 12:43:42 2010 @@ -1140,9 +1140,10 @@ /* Omitted type specifier. Should be like str(self). */ type = 'g'; default_precision = PyFloat_STR_PRECISION; - add_parens = 1; - if (re == 0.0) + if (re == 0.0 && copysign(1.0, re) == 1.0) skip_re = 1; + else + add_parens = 1; } if (type == 'n') @@ -1234,8 +1235,11 @@ n_re_digits, n_re_remainder, re_has_decimal, &locale, &tmp_format); - /* Same formatting, but always include a sign. */ - tmp_format.sign = '+'; + /* Same formatting, but always include a sign, unless the real part is + * going to be omitted, in which case we use whatever sign convention was + * requested by the original format. */ + if (!skip_re) + tmp_format.sign = '+'; n_im_total = calc_number_widths(&im_spec, 0, im_sign_char, p_im, n_im_digits, n_im_remainder, im_has_decimal, &locale, &tmp_format); From python-checkins at python.org Sun Aug 1 12:45:15 2010 From: python-checkins at python.org (mark.dickinson) Date: Sun, 1 Aug 2010 12:45:15 +0200 (CEST) Subject: [Python-checkins] r83402 - in python/branches/release27-maint: Lib/test/test_complex.py Misc/NEWS Objects/stringlib/formatter.h Message-ID: <20100801104515.EEC26EE984@mail.python.org> Author: mark.dickinson Date: Sun Aug 1 12:45:15 2010 New Revision: 83402 Log: Merged revisions 83400 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83400 | mark.dickinson | 2010-08-01 11:41:49 +0100 (Sun, 01 Aug 2010) | 7 lines Issue #9416: Fix some issues with complex formatting where the output with no type specifier failed to match the str output: - format(complex(-0.0, 2.0), '-') omitted the real part from the output, - format(complex(0.0, 2.0), '-') included a sign and parentheses. ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Lib/test/test_complex.py python/branches/release27-maint/Misc/NEWS python/branches/release27-maint/Objects/stringlib/formatter.h Modified: python/branches/release27-maint/Lib/test/test_complex.py ============================================================================== --- python/branches/release27-maint/Lib/test/test_complex.py (original) +++ python/branches/release27-maint/Lib/test/test_complex.py Sun Aug 1 12:45:15 2010 @@ -558,6 +558,16 @@ self.assertEqual(format(z, '-'), str(z)) self.assertEqual(format(z, '<'), str(z)) self.assertEqual(format(z, '10'), str(z)) + z = complex(0.0, 3.0) + self.assertEqual(format(z, ''), str(z)) + self.assertEqual(format(z, '-'), str(z)) + self.assertEqual(format(z, '<'), str(z)) + self.assertEqual(format(z, '2'), str(z)) + z = complex(-0.0, 2.0) + self.assertEqual(format(z, ''), str(z)) + self.assertEqual(format(z, '-'), str(z)) + self.assertEqual(format(z, '<'), str(z)) + self.assertEqual(format(z, '3'), str(z)) self.assertEqual(format(1+3j, 'g'), '1+3j') self.assertEqual(format(3j, 'g'), '0+3j') Modified: python/branches/release27-maint/Misc/NEWS ============================================================================== --- python/branches/release27-maint/Misc/NEWS (original) +++ python/branches/release27-maint/Misc/NEWS Sun Aug 1 12:45:15 2010 @@ -12,6 +12,12 @@ Core and Builtins ----------------- +- Issue #9416: Fix some issues with complex formatting where the + output with no type specifier failed to match the str output: + + - format(complex(-0.0, 2.0), '-') omitted the real part from the output, + - format(complex(0.0, 2.0), '-') included a sign and parentheses. + - Issue #7616: Fix copying of overlapping memoryview slices with the Intel compiler. Modified: python/branches/release27-maint/Objects/stringlib/formatter.h ============================================================================== --- python/branches/release27-maint/Objects/stringlib/formatter.h (original) +++ python/branches/release27-maint/Objects/stringlib/formatter.h Sun Aug 1 12:45:15 2010 @@ -1144,9 +1144,10 @@ /* Omitted type specifier. Should be like str(self). */ type = 'g'; default_precision = PyFloat_STR_PRECISION; - add_parens = 1; - if (re == 0.0) + if (re == 0.0 && copysign(1.0, re) == 1.0) skip_re = 1; + else + add_parens = 1; } if (type == 'n') @@ -1231,8 +1232,11 @@ n_re_digits, n_re_remainder, re_has_decimal, &locale, &tmp_format); - /* Same formatting, but always include a sign. */ - tmp_format.sign = '+'; + /* Same formatting, but always include a sign, unless the real part is + * going to be omitted, in which case we use whatever sign convention was + * requested by the original format. */ + if (!skip_re) + tmp_format.sign = '+'; n_im_total = calc_number_widths(&im_spec, 0, im_sign_char, p_im, n_im_digits, n_im_remainder, im_has_decimal, &locale, &tmp_format); From python-checkins at python.org Sun Aug 1 13:10:28 2010 From: python-checkins at python.org (mark.dickinson) Date: Sun, 1 Aug 2010 13:10:28 +0200 (CEST) Subject: [Python-checkins] r83403 - python/branches/py3k/Lib/test/test_struct.py Message-ID: <20100801111028.5400FEED5E@mail.python.org> Author: mark.dickinson Date: Sun Aug 1 13:10:28 2010 New Revision: 83403 Log: Add test for memory leak reported in issue 9422. Modified: python/branches/py3k/Lib/test/test_struct.py Modified: python/branches/py3k/Lib/test/test_struct.py ============================================================================== --- python/branches/py3k/Lib/test/test_struct.py (original) +++ python/branches/py3k/Lib/test/test_struct.py Sun Aug 1 13:10:28 2010 @@ -560,7 +560,12 @@ 'spam and eggs') self.assertRaises(struct.error, struct.unpack_from, '14s42', store, 0) - + def test_Struct_reinitialization(self): + # Issue 9422: there was a memory leak when reinitializing a + # Struct instance. This test can be used to detect the leak + # when running with regrtest -L. + s = struct.Struct('i') + s.__init__('ii') def test_main(): run_unittest(StructTest) From python-checkins at python.org Sun Aug 1 16:25:22 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 1 Aug 2010 16:25:22 +0200 (CEST) Subject: [Python-checkins] r83404 - in python/branches/py3k/Demo/embed: Makefile demo.c loop.c Message-ID: <20100801142522.54A8FEE984@mail.python.org> Author: georg.brandl Date: Sun Aug 1 16:25:22 2010 New Revision: 83404 Log: #6439: fix argument type for PySys_SetArgvEx() and Py_SetProgramName() in Demo/embed code. Modified: python/branches/py3k/Demo/embed/Makefile python/branches/py3k/Demo/embed/demo.c python/branches/py3k/Demo/embed/loop.c Modified: python/branches/py3k/Demo/embed/Makefile ============================================================================== --- python/branches/py3k/Demo/embed/Makefile (original) +++ python/branches/py3k/Demo/embed/Makefile Sun Aug 1 16:25:22 2010 @@ -22,7 +22,7 @@ LIBPYTHON= $(blddir)/libpython$(VERSION).a # XXX edit LIBS (in particular) to match $(blddir)/Makefile -LIBS= -lnsl -ldl -lreadline -ltermcap -lieee -lpthread -lutil +LIBS= -lnsl -ldl -lreadline -lieee -lpthread -lutil LDFLAGS= -Xlinker -export-dynamic SYSLIBS= -lm MODLIBS= Modified: python/branches/py3k/Demo/embed/demo.c ============================================================================== --- python/branches/py3k/Demo/embed/demo.c (original) +++ python/branches/py3k/Demo/embed/demo.c Sun Aug 1 16:25:22 2010 @@ -22,7 +22,7 @@ /* Define sys.argv. It is up to the application if you want this; you can also leave it undefined (since the Python code is generally not a main program it has no business - touching sys.argv...) + touching sys.argv...) If the third argument is true, sys.path is modified to include either the directory containing the script named by argv[0], or @@ -32,7 +32,7 @@ directory (say, a file named os.py) that your application would then import and run. */ - PySys_SetArgvEx(argc, argv, 0); + PySys_SetArgvEx(2, args, 0); /* Do some application specific code */ printf("Hello, brave new world\n\n"); Modified: python/branches/py3k/Demo/embed/loop.c ============================================================================== --- python/branches/py3k/Demo/embed/loop.c (original) +++ python/branches/py3k/Demo/embed/loop.c Sun Aug 1 16:25:22 2010 @@ -19,7 +19,7 @@ count = atoi(argv[2]); } - Py_SetProgramName(argv[0]); + Py_SetProgramName(L"loop"); /* uncomment this if you don't want to load site.py */ /* Py_NoSiteFlag = 1; */ From python-checkins at python.org Sun Aug 1 16:38:17 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 1 Aug 2010 16:38:17 +0200 (CEST) Subject: [Python-checkins] r83405 - python/branches/py3k/Lib/trace.py Message-ID: <20100801143817.9189AEE987@mail.python.org> Author: georg.brandl Date: Sun Aug 1 16:38:17 2010 New Revision: 83405 Log: #4943: do not try to include drive letters (and colons) when looking for a probably module name. Modified: python/branches/py3k/Lib/trace.py Modified: python/branches/py3k/Lib/trace.py ============================================================================== --- python/branches/py3k/Lib/trace.py (original) +++ python/branches/py3k/Lib/trace.py Sun Aug 1 16:38:17 2010 @@ -192,11 +192,13 @@ base = path[len(longest) + 1:] else: base = path + # the drive letter is never part of the module name + drive, base = os.path.splitdrive(base) base = base.replace(os.sep, ".") if os.altsep: base = base.replace(os.altsep, ".") filename, ext = os.path.splitext(base) - return filename + return filename.lstrip(".") class CoverageResults: def __init__(self, counts=None, calledfuncs=None, infile=None, From python-checkins at python.org Sun Aug 1 16:50:00 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 1 Aug 2010 16:50:00 +0200 (CEST) Subject: [Python-checkins] r83406 - in python/branches/py3k: Doc/library/mmap.rst Lib/test/test_mmap.py Misc/NEWS Modules/mmapmodule.c Message-ID: <20100801145000.A7341EE999@mail.python.org> Author: georg.brandl Date: Sun Aug 1 16:50:00 2010 New Revision: 83406 Log: #8046: add context manager protocol support to mmap objects. Also add closed property. Modified: python/branches/py3k/Doc/library/mmap.rst python/branches/py3k/Lib/test/test_mmap.py python/branches/py3k/Misc/NEWS python/branches/py3k/Modules/mmapmodule.c Modified: python/branches/py3k/Doc/library/mmap.rst ============================================================================== --- python/branches/py3k/Doc/library/mmap.rst (original) +++ python/branches/py3k/Doc/library/mmap.rst Sun Aug 1 16:50:00 2010 @@ -112,6 +112,18 @@ map.close() + :class:`mmap` can also be used as a context manager in a :keyword:`with` + statement.:: + + import mmap + + with mmap.mmap(-1, 13) as map: + map.write("Hello world!") + + .. versionadded:: 3.2 + Context manager support. + + The next example demonstrates how to create an anonymous map and exchange data between the parent and child processes:: @@ -132,13 +144,19 @@ Memory-mapped file objects support the following methods: - .. method:: close() Close the file. Subsequent calls to other methods of the object will result in an exception being raised. + .. attribute:: closed + + True if the file is closed. + + .. versionadded:: 3.2 + + .. method:: find(sub[, start[, end]]) Returns the lowest index in the object where the subsequence *sub* is @@ -236,5 +254,3 @@ position of the file pointer; the file position is advanced by ``1``. If the mmap was created with :const:`ACCESS_READ`, then writing to it will throw a :exc:`TypeError` exception. - - Modified: python/branches/py3k/Lib/test/test_mmap.py ============================================================================== --- python/branches/py3k/Lib/test/test_mmap.py (original) +++ python/branches/py3k/Lib/test/test_mmap.py Sun Aug 1 16:50:00 2010 @@ -586,6 +586,20 @@ pass m.close() + def test_context_manager(self): + with mmap.mmap(-1, 10) as m: + self.assertFalse(m.closed) + self.assertTrue(m.closed) + + def test_context_manager_exception(self): + # Test that the IOError gets passed through + with self.assertRaises(Exception) as exc: + with mmap.mmap(-1, 10) as m: + raise IOError + self.assertIsInstance(exc.exception, IOError, + "wrong exception raised in context manager") + self.assertTrue(m.closed, "context manager failed") + def test_main(): run_unittest(MmapTests) Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Sun Aug 1 16:50:00 2010 @@ -18,6 +18,12 @@ - format(complex(-0.0, 2.0), '-') omitted the real part from the output, - format(complex(0.0, 2.0), '-') included a sign and parentheses. +Extensions +---------- + +- Issue #8046: Add context manager protocol support and .closed property + to mmap objects. + Library ------- Modified: python/branches/py3k/Modules/mmapmodule.c ============================================================================== --- python/branches/py3k/Modules/mmapmodule.c (original) +++ python/branches/py3k/Modules/mmapmodule.c Sun Aug 1 16:50:00 2010 @@ -651,6 +651,31 @@ } } +static PyObject * +mmap_closed_get(mmap_object *self) +{ +#ifdef MS_WINDOWS + return PyBool_FromLong(self->map_handle == NULL ? 1 : 0); +#elif defined(UNIX) + return PyBool_FromLong(self->data == NULL ? 1 : 0); +#endif +} + +static PyObject * +mmap__enter__method(mmap_object *self, PyObject *args) +{ + CHECK_VALID(NULL); + + Py_INCREF(self); + return (PyObject *)self; +} + +static PyObject * +mmap__exit__method(PyObject *self, PyObject *args) +{ + return PyObject_CallMethod(self, "close", NULL); +} + static struct PyMethodDef mmap_object_methods[] = { {"close", (PyCFunction) mmap_close_method, METH_NOARGS}, {"find", (PyCFunction) mmap_find_method, METH_VARARGS}, @@ -666,9 +691,17 @@ {"tell", (PyCFunction) mmap_tell_method, METH_NOARGS}, {"write", (PyCFunction) mmap_write_method, METH_VARARGS}, {"write_byte", (PyCFunction) mmap_write_byte_method, METH_VARARGS}, + {"__enter__", (PyCFunction) mmap__enter__method, METH_NOARGS}, + {"__exit__", (PyCFunction) mmap__exit__method, METH_VARARGS}, {NULL, NULL} /* sentinel */ }; +static PyGetSetDef mmap_object_getset[] = { + {"closed", (getter) mmap_closed_get, NULL, NULL}, + {NULL} +}; + + /* Functions for treating an mmap'ed file as a buffer */ static int @@ -975,7 +1008,7 @@ 0, /* tp_iternext */ mmap_object_methods, /* tp_methods */ 0, /* tp_members */ - 0, /* tp_getset */ + mmap_object_getset, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ From python-checkins at python.org Sun Aug 1 17:26:26 2010 From: python-checkins at python.org (brian.curtin) Date: Sun, 1 Aug 2010 17:26:26 +0200 (CEST) Subject: [Python-checkins] r83407 - in python/branches/py3k: Lib/test/test_mmap.py Misc/NEWS Modules/mmapmodule.c Message-ID: <20100801152626.E3C45EEBFE@mail.python.org> Author: brian.curtin Date: Sun Aug 1 17:26:26 2010 New Revision: 83407 Log: Fix #8105. Add validation to mmap.mmap so invalid file descriptors don't cause a crash on Windows. Modified: python/branches/py3k/Lib/test/test_mmap.py python/branches/py3k/Misc/NEWS python/branches/py3k/Modules/mmapmodule.c Modified: python/branches/py3k/Lib/test/test_mmap.py ============================================================================== --- python/branches/py3k/Lib/test/test_mmap.py (original) +++ python/branches/py3k/Lib/test/test_mmap.py Sun Aug 1 17:26:26 2010 @@ -1,6 +1,6 @@ from test.support import TESTFN, run_unittest, import_module import unittest -import os, re, itertools +import os, re, itertools, socket # Skip test if we can't import mmap. mmap = import_module('mmap') @@ -586,6 +586,17 @@ pass m.close() + def test_invalid_descriptor(self): + # socket file descriptors are valid, but out of range + # for _get_osfhandle, causing a crash when validating the + # parameters to _get_osfhandle. + s = socket.socket() + try: + with self.assertRaises(mmap.error): + m = mmap.mmap(s.fileno(), 10) + finally: + s.close() + def test_context_manager(self): with mmap.mmap(-1, 10) as m: self.assertFalse(m.closed) Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Sun Aug 1 17:26:26 2010 @@ -21,6 +21,8 @@ Extensions ---------- +- Issue #8105: Validate file descriptor passed to mmap.mmap on Windows. + - Issue #8046: Add context manager protocol support and .closed property to mmap objects. Modified: python/branches/py3k/Modules/mmapmodule.c ============================================================================== --- python/branches/py3k/Modules/mmapmodule.c (original) +++ python/branches/py3k/Modules/mmapmodule.c Sun Aug 1 17:26:26 2010 @@ -1236,6 +1236,11 @@ 1); */ if (fileno != -1 && fileno != 0) { + /* Ensure that fileno is within the CRT's valid range */ + if (_PyVerify_fd(fileno) == 0) { + PyErr_SetFromErrno(mmap_module_error); + return NULL; + } fh = (HANDLE)_get_osfhandle(fileno); if (fh==(HANDLE)-1) { PyErr_SetFromErrno(mmap_module_error); From python-checkins at python.org Sun Aug 1 17:30:56 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 1 Aug 2010 17:30:56 +0200 (CEST) Subject: [Python-checkins] r83408 - in python/branches/py3k: Lib/posixpath.py Misc/NEWS Message-ID: <20100801153056.8787EEE98E@mail.python.org> Author: georg.brandl Date: Sun Aug 1 17:30:56 2010 New Revision: 83408 Log: #5551: symbolic links never can be mount points. Fixes the fix for #1713. Modified: python/branches/py3k/Lib/posixpath.py python/branches/py3k/Misc/NEWS Modified: python/branches/py3k/Lib/posixpath.py ============================================================================== --- python/branches/py3k/Lib/posixpath.py (original) +++ python/branches/py3k/Lib/posixpath.py Sun Aug 1 17:30:56 2010 @@ -200,6 +200,9 @@ def ismount(path): """Test whether a path is a mount point""" + if islink(path): + # A symlink can never be a mount point + return False try: s1 = os.lstat(path) if isinstance(path, bytes): Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Sun Aug 1 17:30:56 2010 @@ -29,6 +29,9 @@ Library ------- +- Issue #1713: Fix os.path.ismount(), which returned true for symbolic links + across devices. + - Issue #8826: Properly load old-style "expires" attribute in http.cookies. - Issue #1690103: Fix initial namespace for code run with trace.main(). From python-checkins at python.org Sun Aug 1 17:44:11 2010 From: python-checkins at python.org (brian.curtin) Date: Sun, 1 Aug 2010 17:44:11 +0200 (CEST) Subject: [Python-checkins] r83409 - in python/branches/release31-maint: Lib/test/test_mmap.py Misc/NEWS Modules/mmapmodule.c Message-ID: <20100801154411.9B957EECDE@mail.python.org> Author: brian.curtin Date: Sun Aug 1 17:44:11 2010 New Revision: 83409 Log: Merged revisions 83407 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83407 | brian.curtin | 2010-08-01 10:26:26 -0500 (Sun, 01 Aug 2010) | 3 lines Fix #8105. Add validation to mmap.mmap so invalid file descriptors don't cause a crash on Windows. ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Lib/test/test_mmap.py python/branches/release31-maint/Misc/NEWS python/branches/release31-maint/Modules/mmapmodule.c Modified: python/branches/release31-maint/Lib/test/test_mmap.py ============================================================================== --- python/branches/release31-maint/Lib/test/test_mmap.py (original) +++ python/branches/release31-maint/Lib/test/test_mmap.py Sun Aug 1 17:44:11 2010 @@ -1,6 +1,6 @@ from test.support import TESTFN, run_unittest, import_module import unittest -import os, re, itertools +import os, re, itertools, socket # Skip test if we can't import mmap. mmap = import_module('mmap') @@ -586,6 +586,16 @@ pass m.close() + def test_invalid_descriptor(self): + # socket file descriptors are valid, but out of range + # for _get_osfhandle, causing a crash when validating the + # parameters to _get_osfhandle. + s = socket.socket() + try: + with self.assertRaises(mmap.error): + m = mmap.mmap(s.fileno(), 10) + finally: + s.close() def test_main(): run_unittest(MmapTests) Modified: python/branches/release31-maint/Misc/NEWS ============================================================================== --- python/branches/release31-maint/Misc/NEWS (original) +++ python/branches/release31-maint/Misc/NEWS Sun Aug 1 17:44:11 2010 @@ -332,6 +332,8 @@ Extension Modules ----------------- +- Issue #8105: Validate file descriptor passed to mmap.mmap on Windows. + - Issue #9422: Fix memory leak when re-initializing a struct.Struct object. - Issue #7900: The getgroups(2) system call on MacOSX behaves rather oddly Modified: python/branches/release31-maint/Modules/mmapmodule.c ============================================================================== --- python/branches/release31-maint/Modules/mmapmodule.c (original) +++ python/branches/release31-maint/Modules/mmapmodule.c Sun Aug 1 17:44:11 2010 @@ -1203,6 +1203,11 @@ 1); */ if (fileno != -1 && fileno != 0) { + /* Ensure that fileno is within the CRT's valid range */ + if (_PyVerify_fd(fileno) == 0) { + PyErr_SetFromErrno(mmap_module_error); + return NULL; + } fh = (HANDLE)_get_osfhandle(fileno); if (fh==(HANDLE)-1) { PyErr_SetFromErrno(mmap_module_error); From python-checkins at python.org Sun Aug 1 17:47:53 2010 From: python-checkins at python.org (brian.curtin) Date: Sun, 1 Aug 2010 17:47:53 +0200 (CEST) Subject: [Python-checkins] r83410 - in python/branches/release27-maint: Lib/test/test_mmap.py Misc/NEWS Modules/mmapmodule.c Message-ID: <20100801154753.A9BDDEEC3A@mail.python.org> Author: brian.curtin Date: Sun Aug 1 17:47:53 2010 New Revision: 83410 Log: Merged revisions 83407 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83407 | brian.curtin | 2010-08-01 10:26:26 -0500 (Sun, 01 Aug 2010) | 3 lines Fix #8105. Add validation to mmap.mmap so invalid file descriptors don't cause a crash on Windows. ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Lib/test/test_mmap.py python/branches/release27-maint/Misc/NEWS python/branches/release27-maint/Modules/mmapmodule.c Modified: python/branches/release27-maint/Lib/test/test_mmap.py ============================================================================== --- python/branches/release27-maint/Lib/test/test_mmap.py (original) +++ python/branches/release27-maint/Lib/test/test_mmap.py Sun Aug 1 17:47:53 2010 @@ -1,6 +1,6 @@ from test.test_support import TESTFN, run_unittest, import_module import unittest -import os, re, itertools +import os, re, itertools, socket mmap = import_module('mmap') @@ -586,6 +586,16 @@ pass m.close() + def test_invalid_descriptor(self): + # socket file descriptors are valid, but out of range + # for _get_osfhandle, causing a crash when validating the + # parameters to _get_osfhandle. + s = socket.socket() + try: + with self.assertRaises(mmap.error): + m = mmap.mmap(s.fileno(), 10) + finally: + s.close() def test_main(): run_unittest(MmapTests) Modified: python/branches/release27-maint/Misc/NEWS ============================================================================== --- python/branches/release27-maint/Misc/NEWS (original) +++ python/branches/release27-maint/Misc/NEWS Sun Aug 1 17:47:53 2010 @@ -76,6 +76,8 @@ Extension Modules ----------------- +- Issue #8105: Validate file descriptor passed to mmap.mmap on Windows. + - Issue #1019882: Fix IndexError when loading certain hotshot stats. - Issue #9422: Fix memory leak when re-initializing a struct.Struct object. Modified: python/branches/release27-maint/Modules/mmapmodule.c ============================================================================== --- python/branches/release27-maint/Modules/mmapmodule.c (original) +++ python/branches/release27-maint/Modules/mmapmodule.c Sun Aug 1 17:47:53 2010 @@ -1280,6 +1280,11 @@ "don't use 0 for anonymous memory"); */ if (fileno != -1 && fileno != 0) { + /* Ensure that fileno is within the CRT's valid range */ + if (_PyVerify_fd(fileno) == 0) { + PyErr_SetFromErrno(mmap_module_error); + return NULL; + } fh = (HANDLE)_get_osfhandle(fileno); if (fh==(HANDLE)-1) { PyErr_SetFromErrno(mmap_module_error); From python-checkins at python.org Sun Aug 1 18:53:42 2010 From: python-checkins at python.org (antoine.pitrou) Date: Sun, 1 Aug 2010 18:53:42 +0200 (CEST) Subject: [Python-checkins] r83411 - in python/branches/py3k: Misc/NEWS Modules/_io/bufferedio.c Message-ID: <20100801165342.CBE03EE988@mail.python.org> Author: antoine.pitrou Date: Sun Aug 1 18:53:42 2010 New Revision: 83411 Log: Issue #9448: Fix a leak of OS resources (mutexes or semaphores) when re-initializing a buffered IO object by calling its `__init__` method. Modified: python/branches/py3k/Misc/NEWS python/branches/py3k/Modules/_io/bufferedio.c Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Sun Aug 1 18:53:42 2010 @@ -29,6 +29,9 @@ Library ------- +- Issue #9448: Fix a leak of OS resources (mutexes or semaphores) when + re-initializing a buffered IO object by calling its ``__init__`` method. + - Issue #1713: Fix os.path.ismount(), which returned true for symbolic links across devices. Modified: python/branches/py3k/Modules/_io/bufferedio.c ============================================================================== --- python/branches/py3k/Modules/_io/bufferedio.c (original) +++ python/branches/py3k/Modules/_io/bufferedio.c Sun Aug 1 18:53:42 2010 @@ -636,6 +636,8 @@ return -1; } #ifdef WITH_THREAD + if (self->lock) + PyThread_free_lock(self->lock); self->lock = PyThread_allocate_lock(); if (self->lock == NULL) { PyErr_SetString(PyExc_RuntimeError, "can't allocate read lock"); From python-checkins at python.org Sun Aug 1 18:57:17 2010 From: python-checkins at python.org (antoine.pitrou) Date: Sun, 1 Aug 2010 18:57:17 +0200 (CEST) Subject: [Python-checkins] r83412 - in python/branches/release27-maint: Misc/NEWS Modules/_io/bufferedio.c Message-ID: <20100801165717.8456BEE98E@mail.python.org> Author: antoine.pitrou Date: Sun Aug 1 18:57:17 2010 New Revision: 83412 Log: Merged revisions 83411 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83411 | antoine.pitrou | 2010-08-01 18:53:42 +0200 (dim., 01 ao?t 2010) | 4 lines Issue #9448: Fix a leak of OS resources (mutexes or semaphores) when re-initializing a buffered IO object by calling its `__init__` method. ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Misc/NEWS python/branches/release27-maint/Modules/_io/bufferedio.c Modified: python/branches/release27-maint/Misc/NEWS ============================================================================== --- python/branches/release27-maint/Misc/NEWS (original) +++ python/branches/release27-maint/Misc/NEWS Sun Aug 1 18:57:17 2010 @@ -24,6 +24,9 @@ Library ------- +- Issue #9448: Fix a leak of OS resources (mutexes or semaphores) when + re-initializing a buffered IO object by calling its ``__init__`` method. + - Issue #8471: In doctest, properly reset the output stream to an empty string when Unicode was previously output. Modified: python/branches/release27-maint/Modules/_io/bufferedio.c ============================================================================== --- python/branches/release27-maint/Modules/_io/bufferedio.c (original) +++ python/branches/release27-maint/Modules/_io/bufferedio.c Sun Aug 1 18:57:17 2010 @@ -636,6 +636,8 @@ return -1; } #ifdef WITH_THREAD + if (self->lock) + PyThread_free_lock(self->lock); self->lock = PyThread_allocate_lock(); if (self->lock == NULL) { PyErr_SetString(PyExc_RuntimeError, "can't allocate read lock"); From python-checkins at python.org Sun Aug 1 18:57:42 2010 From: python-checkins at python.org (antoine.pitrou) Date: Sun, 1 Aug 2010 18:57:42 +0200 (CEST) Subject: [Python-checkins] r83413 - in python/branches/release31-maint: Misc/NEWS Modules/_io/bufferedio.c Message-ID: <20100801165742.3282EEE999@mail.python.org> Author: antoine.pitrou Date: Sun Aug 1 18:57:42 2010 New Revision: 83413 Log: Merged revisions 83411 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83411 | antoine.pitrou | 2010-08-01 18:53:42 +0200 (dim., 01 ao?t 2010) | 4 lines Issue #9448: Fix a leak of OS resources (mutexes or semaphores) when re-initializing a buffered IO object by calling its `__init__` method. ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Misc/NEWS python/branches/release31-maint/Modules/_io/bufferedio.c Modified: python/branches/release31-maint/Misc/NEWS ============================================================================== --- python/branches/release31-maint/Misc/NEWS (original) +++ python/branches/release31-maint/Misc/NEWS Sun Aug 1 18:57:42 2010 @@ -84,6 +84,9 @@ Library ------- +- Issue #9448: Fix a leak of OS resources (mutexes or semaphores) when + re-initializing a buffered IO object by calling its ``__init__`` method. + - Issue #8620: when a Cmd is fed input that reaches EOF without a final newline, it no longer truncates the last character of the last command line. Modified: python/branches/release31-maint/Modules/_io/bufferedio.c ============================================================================== --- python/branches/release31-maint/Modules/_io/bufferedio.c (original) +++ python/branches/release31-maint/Modules/_io/bufferedio.c Sun Aug 1 18:57:42 2010 @@ -632,6 +632,8 @@ return -1; } #ifdef WITH_THREAD + if (self->lock) + PyThread_free_lock(self->lock); self->lock = PyThread_allocate_lock(); if (self->lock == NULL) { PyErr_SetString(PyExc_RuntimeError, "can't allocate read lock"); From python-checkins at python.org Sun Aug 1 18:59:33 2010 From: python-checkins at python.org (antoine.pitrou) Date: Sun, 1 Aug 2010 18:59:33 +0200 (CEST) Subject: [Python-checkins] r83414 - python/branches/release26-maint Message-ID: <20100801165933.2BF7DEE994@mail.python.org> Author: antoine.pitrou Date: Sun Aug 1 18:59:29 2010 New Revision: 83414 Log: Blocked revisions 83412 via svnmerge ................ r83412 | antoine.pitrou | 2010-08-01 18:57:17 +0200 (dim., 01 ao?t 2010) | 10 lines Merged revisions 83411 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83411 | antoine.pitrou | 2010-08-01 18:53:42 +0200 (dim., 01 ao?t 2010) | 4 lines Issue #9448: Fix a leak of OS resources (mutexes or semaphores) when re-initializing a buffered IO object by calling its `__init__` method. ........ ................ Modified: python/branches/release26-maint/ (props changed) From python-checkins at python.org Sun Aug 1 19:53:38 2010 From: python-checkins at python.org (senthil.kumaran) Date: Sun, 1 Aug 2010 19:53:38 +0200 (CEST) Subject: [Python-checkins] r83415 - in python/branches/py3k/Lib: test/test_urllib.py urllib/request.py Message-ID: <20100801175338.24577EEAFD@mail.python.org> Author: senthil.kumaran Date: Sun Aug 1 19:53:37 2010 New Revision: 83415 Log: Fix Issue8123 - TypeError in urllib when trying to use HTTP authentication Modified: python/branches/py3k/Lib/test/test_urllib.py python/branches/py3k/Lib/urllib/request.py Modified: python/branches/py3k/Lib/test/test_urllib.py ============================================================================== --- python/branches/py3k/Lib/test/test_urllib.py (original) +++ python/branches/py3k/Lib/test/test_urllib.py Sun Aug 1 19:53:37 2010 @@ -190,6 +190,17 @@ finally: self.unfakehttp() + def test_userpass_inurl(self): + self.fakehttp(b"Hello!") + try: + fp = urlopen("http://user:pass at python.org/") + self.assertEqual(fp.readline(), b"Hello!") + self.assertEqual(fp.readline(), b"") + self.assertEqual(fp.geturl(), 'http://user:pass at python.org/') + self.assertEqual(fp.getcode(), 200) + finally: + self.unfakehttp() + class urlretrieve_FileTests(unittest.TestCase): """Test urllib.urlretrieve() on local files""" Modified: python/branches/py3k/Lib/urllib/request.py ============================================================================== --- python/branches/py3k/Lib/urllib/request.py (original) +++ python/branches/py3k/Lib/urllib/request.py Sun Aug 1 19:53:37 2010 @@ -1591,13 +1591,13 @@ if proxy_passwd: import base64 - proxy_auth = base64.b64encode(proxy_passwd).strip() + proxy_auth = base64.b64encode(proxy_passwd.encode()).strip() else: proxy_auth = None if user_passwd: import base64 - auth = base64.b64encode(user_passwd).strip() + auth = base64.b64encode(user_passwd.encode()).strip() else: auth = None http_conn = connection_factory(host) From python-checkins at python.org Sun Aug 1 19:55:50 2010 From: python-checkins at python.org (senthil.kumaran) Date: Sun, 1 Aug 2010 19:55:50 +0200 (CEST) Subject: [Python-checkins] r83416 - in python/branches/release31-maint: Lib/test/test_urllib.py Lib/urllib/request.py Message-ID: <20100801175550.44AC5EEAF0@mail.python.org> Author: senthil.kumaran Date: Sun Aug 1 19:55:50 2010 New Revision: 83416 Log: Merged revisions 83415 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83415 | senthil.kumaran | 2010-08-01 23:23:37 +0530 (Sun, 01 Aug 2010) | 3 lines Fix Issue8123 - TypeError in urllib when trying to use HTTP authentication ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Lib/test/test_urllib.py python/branches/release31-maint/Lib/urllib/request.py Modified: python/branches/release31-maint/Lib/test/test_urllib.py ============================================================================== --- python/branches/release31-maint/Lib/test/test_urllib.py (original) +++ python/branches/release31-maint/Lib/test/test_urllib.py Sun Aug 1 19:55:50 2010 @@ -191,6 +191,17 @@ finally: self.unfakehttp() + def test_userpass_inurl(self): + self.fakehttp(b"Hello!") + try: + fp = urlopen("http://user:pass at python.org/") + self.assertEqual(fp.readline(), b"Hello!") + self.assertEqual(fp.readline(), b"") + self.assertEqual(fp.geturl(), 'http://user:pass at python.org/') + self.assertEqual(fp.getcode(), 200) + finally: + self.unfakehttp() + class urlretrieve_FileTests(unittest.TestCase): """Test urllib.urlretrieve() on local files""" Modified: python/branches/release31-maint/Lib/urllib/request.py ============================================================================== --- python/branches/release31-maint/Lib/urllib/request.py (original) +++ python/branches/release31-maint/Lib/urllib/request.py Sun Aug 1 19:55:50 2010 @@ -1595,13 +1595,13 @@ if proxy_passwd: import base64 - proxy_auth = base64.b64encode(proxy_passwd).strip() + proxy_auth = base64.b64encode(proxy_passwd.encode()).strip() else: proxy_auth = None if user_passwd: import base64 - auth = base64.b64encode(user_passwd).strip() + auth = base64.b64encode(user_passwd.encode()).strip() else: auth = None http_conn = connection_factory(host) From python-checkins at python.org Sun Aug 1 20:38:26 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 1 Aug 2010 20:38:26 +0200 (CEST) Subject: [Python-checkins] r83417 - python/branches/py3k/Misc/RPM/python-3.2.spec Message-ID: <20100801183826.61AD5EE983@mail.python.org> Author: georg.brandl Date: Sun Aug 1 20:38:26 2010 New Revision: 83417 Log: #5776: fix mistakes in python specfile. (Nobody probably uses it anyway.) Modified: python/branches/py3k/Misc/RPM/python-3.2.spec Modified: python/branches/py3k/Misc/RPM/python-3.2.spec ============================================================================== --- python/branches/py3k/Misc/RPM/python-3.2.spec (original) +++ python/branches/py3k/Misc/RPM/python-3.2.spec Sun Aug 1 20:38:26 2010 @@ -40,7 +40,7 @@ %define name python #--start constants-- %define version 3.2a1 -%define libver 3.2 +%define libvers 3.2 #--end constants-- %define release 1pydotorg %define __prefix /usr @@ -61,7 +61,7 @@ Name: %{name}%{binsuffix} Version: %{version} Release: %{release} -License: Modified CNRI Open Source License +License: PSF Group: Development/Languages Source: Python-%{version}.tar.bz2 %if %{include_docs} From python-checkins at python.org Sun Aug 1 20:41:59 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 1 Aug 2010 20:41:59 +0200 (CEST) Subject: [Python-checkins] r83418 - in python/branches/release27-maint: Objects/unicodeobject.c Message-ID: <20100801184159.EA5D0EEA6D@mail.python.org> Author: georg.brandl Date: Sun Aug 1 20:41:59 2010 New Revision: 83418 Log: Merged revisions 83395 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83395 | georg.brandl | 2010-08-01 10:49:18 +0200 (So, 01 Aug 2010) | 1 line #8821: do not rely on Unicode strings being terminated with a \u0000, rather explicitly check range before looking for a second surrogate character. ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Objects/unicodeobject.c Modified: python/branches/release27-maint/Objects/unicodeobject.c ============================================================================== --- python/branches/release27-maint/Objects/unicodeobject.c (original) +++ python/branches/release27-maint/Objects/unicodeobject.c Sun Aug 1 20:41:59 2010 @@ -3067,7 +3067,7 @@ ch2 = *s++; size--; - if (ch2 >= 0xDC00 && ch2 <= 0xDFFF) { + if (ch2 >= 0xDC00 && ch2 <= 0xDFFF && size) { ucs = (((ch & 0x03FF) << 10) | (ch2 & 0x03FF)) + 0x00010000; *p++ = '\\'; *p++ = 'U'; @@ -3316,7 +3316,7 @@ ch2 = *s++; size--; - if (ch2 >= 0xDC00 && ch2 <= 0xDFFF) { + if (ch2 >= 0xDC00 && ch2 <= 0xDFFF && size) { ucs = (((ch & 0x03FF) << 10) | (ch2 & 0x03FF)) + 0x00010000; *p++ = '\\'; *p++ = 'U'; From python-checkins at python.org Sun Aug 1 20:43:29 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 1 Aug 2010 20:43:29 +0200 (CEST) Subject: [Python-checkins] r83419 - in python/branches/release27-maint: Misc/RPM/python-2.7.spec Message-ID: <20100801184329.2C08CEEA3C@mail.python.org> Author: georg.brandl Date: Sun Aug 1 20:43:29 2010 New Revision: 83419 Log: Merged revisions 83417 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83417 | georg.brandl | 2010-08-01 20:38:26 +0200 (So, 01 Aug 2010) | 1 line #5776: fix mistakes in python specfile. (Nobody probably uses it anyway.) ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Misc/RPM/python-2.7.spec Modified: python/branches/release27-maint/Misc/RPM/python-2.7.spec ============================================================================== --- python/branches/release27-maint/Misc/RPM/python-2.7.spec (original) +++ python/branches/release27-maint/Misc/RPM/python-2.7.spec Sun Aug 1 20:43:29 2010 @@ -40,7 +40,7 @@ %define name python #--start constants-- %define version 2.7.1a0 -%define libver 2.7 +%define libvers 2.7 #--end constants-- %define release 1pydotorg %define __prefix /usr @@ -61,7 +61,7 @@ Name: %{name}%{binsuffix} Version: %{version} Release: %{release} -License: Modified CNRI Open Source License +License: PSF Group: Development/Languages Source: Python-%{version}.tar.bz2 %if %{include_docs} From python-checkins at python.org Sun Aug 1 20:46:05 2010 From: python-checkins at python.org (ronald.oussoren) Date: Sun, 1 Aug 2010 20:46:05 +0200 (CEST) Subject: [Python-checkins] r83420 - python/branches/release26-maint/Lib/test/test_posix.py Message-ID: <20100801184605.5581CEEA3C@mail.python.org> Author: ronald.oussoren Date: Sun Aug 1 20:46:05 2010 New Revision: 83420 Log: A raise of unittest.SkipTest leaked through in the backport for issue7900, and that causes test failes on some platforms. Modified: python/branches/release26-maint/Lib/test/test_posix.py Modified: python/branches/release26-maint/Lib/test/test_posix.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_posix.py (original) +++ python/branches/release26-maint/Lib/test/test_posix.py Sun Aug 1 20:46:05 2010 @@ -308,11 +308,12 @@ shutil.rmtree(base_path) def test_getgroups(self): - with os.popen('id -G') as idg: + with os.popen('id -G 2>/dev/null') as idg: groups = idg.read().strip() if not groups: - raise unittest.SkipTest("need working 'id -G'") + # This test needs 'id -G' + return # The order of groups isn't important, hence the calls # to sorted. From python-checkins at python.org Sun Aug 1 20:52:52 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 1 Aug 2010 20:52:52 +0200 (CEST) Subject: [Python-checkins] r83421 - in python/branches/release27-maint: Lib/Cookie.py Lib/posixpath.py Lib/test/test_cookie.py Lib/timeit.py Lib/trace.py Misc/NEWS Message-ID: <20100801185252.5B7C6EEADE@mail.python.org> Author: georg.brandl Date: Sun Aug 1 20:52:52 2010 New Revision: 83421 Log: Merged revisions 83393,83396,83398,83405,83408 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83393 | georg.brandl | 2010-08-01 10:35:29 +0200 (So, 01 Aug 2010) | 1 line #1690103: fix initial namespace for code run with trace.main(). ........ r83396 | georg.brandl | 2010-08-01 10:52:32 +0200 (So, 01 Aug 2010) | 1 line #4810: document "--" option separator in timeit help. ........ r83398 | georg.brandl | 2010-08-01 11:06:34 +0200 (So, 01 Aug 2010) | 1 line #8826: the "expires" attribute value is a date string with spaces, but apparently not all user-agents put it in quotes. Handle that as a special case. ........ r83405 | georg.brandl | 2010-08-01 16:38:17 +0200 (So, 01 Aug 2010) | 1 line #4943: do not try to include drive letters (and colons) when looking for a probably module name. ........ r83408 | georg.brandl | 2010-08-01 17:30:56 +0200 (So, 01 Aug 2010) | 1 line #5551: symbolic links never can be mount points. Fixes the fix for #1713. ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Lib/Cookie.py python/branches/release27-maint/Lib/posixpath.py python/branches/release27-maint/Lib/test/test_cookie.py python/branches/release27-maint/Lib/timeit.py python/branches/release27-maint/Lib/trace.py python/branches/release27-maint/Misc/NEWS Modified: python/branches/release27-maint/Lib/Cookie.py ============================================================================== --- python/branches/release27-maint/Lib/Cookie.py (original) +++ python/branches/release27-maint/Lib/Cookie.py Sun Aug 1 20:52:52 2010 @@ -534,6 +534,8 @@ r"(?P" # Start of group 'val' r'"(?:[^\\"]|\\.)*"' # Any doublequoted string r"|" # or + r"\w{3},\s[\w\d-]{9,11}\s[\d:]{8}\sGMT" # Special case for "expires" attr + r"|" # or ""+ _LegalCharsPatt +"*" # Any word or empty string r")" # End of group 'val' r"\s*;?" # Probably ending in a semi-colon Modified: python/branches/release27-maint/Lib/posixpath.py ============================================================================== --- python/branches/release27-maint/Lib/posixpath.py (original) +++ python/branches/release27-maint/Lib/posixpath.py Sun Aug 1 20:52:52 2010 @@ -178,6 +178,9 @@ def ismount(path): """Test whether a path is a mount point""" + if islink(path): + # A symlink can never be a mount point + return False try: s1 = os.lstat(path) s2 = os.lstat(join(path, '..')) Modified: python/branches/release27-maint/Lib/test/test_cookie.py ============================================================================== --- python/branches/release27-maint/Lib/test/test_cookie.py (original) +++ python/branches/release27-maint/Lib/test/test_cookie.py Sun Aug 1 20:52:52 2010 @@ -62,6 +62,16 @@ """) + # loading 'expires' + C = Cookie.SimpleCookie() + C.load('Customer="W"; expires=Wed, 01-Jan-2010 00:00:00 GMT') + self.assertEqual(C['Customer']['expires'], + 'Wed, 01-Jan-2010 00:00:00 GMT') + C = Cookie.SimpleCookie() + C.load('Customer="W"; expires=Wed, 01-Jan-98 00:00:00 GMT') + self.assertEqual(C['Customer']['expires'], + 'Wed, 01-Jan-98 00:00:00 GMT') + def test_quoted_meta(self): # Try cookie with quoted meta-data C = Cookie.SimpleCookie() Modified: python/branches/release27-maint/Lib/timeit.py ============================================================================== --- python/branches/release27-maint/Lib/timeit.py (original) +++ python/branches/release27-maint/Lib/timeit.py Sun Aug 1 20:52:52 2010 @@ -9,7 +9,7 @@ Library usage: see the Timer class. Command line usage: - python timeit.py [-n N] [-r N] [-s S] [-t] [-c] [-h] [statement] + python timeit.py [-n N] [-r N] [-s S] [-t] [-c] [-h] [--] [statement] Options: -n/--number N: how many times to execute 'statement' (default: see below) @@ -19,6 +19,7 @@ -c/--clock: use time.clock() (default on Windows) -v/--verbose: print raw timing results; repeat for more digits precision -h/--help: print this usage message and exit + --: separate options from statement, use when statement starts with - statement: statement to be timed (default 'pass') A multi-line statement may be given by specifying each line as a Modified: python/branches/release27-maint/Lib/trace.py ============================================================================== --- python/branches/release27-maint/Lib/trace.py (original) +++ python/branches/release27-maint/Lib/trace.py Sun Aug 1 20:52:52 2010 @@ -195,11 +195,13 @@ base = path[len(longest) + 1:] else: base = path + # the drive letter is never part of the module name + drive, base = os.path.splitdrive(base) base = base.replace(os.sep, ".") if os.altsep: base = base.replace(os.altsep, ".") filename, ext = os.path.splitext(base) - return filename + return filename.lstrip(".") class CoverageResults: def __init__(self, counts=None, calledfuncs=None, infile=None, @@ -798,7 +800,16 @@ ignoredirs=ignore_dirs, infile=counts_file, outfile=counts_file, timing=timing) try: - t.run('execfile(%r)' % (progname,)) + with open(progname) as fp: + code = compile(fp.read(), progname, 'exec') + # try to emulate __main__ namespace as much as possible + globs = { + '__file__': progname, + '__name__': '__main__', + '__package__': None, + '__cached__': None, + } + t.runctx(code, globs, globs) except IOError, err: _err_exit("Cannot run file %r because: %s" % (sys.argv[0], err)) except SystemExit: Modified: python/branches/release27-maint/Misc/NEWS ============================================================================== --- python/branches/release27-maint/Misc/NEWS (original) +++ python/branches/release27-maint/Misc/NEWS Sun Aug 1 20:52:52 2010 @@ -27,6 +27,13 @@ - Issue #9448: Fix a leak of OS resources (mutexes or semaphores) when re-initializing a buffered IO object by calling its ``__init__`` method. +- Issue #1713: Fix os.path.ismount(), which returned true for symbolic links + across devices. + +- Issue #8826: Properly load old-style "expires" attribute in http.cookies. + +- Issue #1690103: Fix initial namespace for code run with trace.main(). + - Issue #8471: In doctest, properly reset the output stream to an empty string when Unicode was previously output. From python-checkins at python.org Sun Aug 1 20:56:31 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 1 Aug 2010 20:56:31 +0200 (CEST) Subject: [Python-checkins] r83422 - in python/branches/release31-maint: Demo/embed/Makefile Demo/embed/demo.c Demo/embed/loop.c Lib/http/cookies.py Lib/posixpath.py Lib/test/test_http_cookies.py Lib/timeit.py Lib/trace.py Misc/NEWS Message-ID: <20100801185631.02D0FEE984@mail.python.org> Author: georg.brandl Date: Sun Aug 1 20:56:30 2010 New Revision: 83422 Log: Merged revisions 83393,83396,83398,83404-83405,83408 via svnmerge from svn+ssh://svn.python.org/python/branches/py3k ........ r83393 | georg.brandl | 2010-08-01 10:35:29 +0200 (So, 01 Aug 2010) | 1 line #1690103: fix initial namespace for code run with trace.main(). ........ r83396 | georg.brandl | 2010-08-01 10:52:32 +0200 (So, 01 Aug 2010) | 1 line #4810: document "--" option separator in timeit help. ........ r83398 | georg.brandl | 2010-08-01 11:06:34 +0200 (So, 01 Aug 2010) | 1 line #8826: the "expires" attribute value is a date string with spaces, but apparently not all user-agents put it in quotes. Handle that as a special case. ........ r83404 | georg.brandl | 2010-08-01 16:25:22 +0200 (So, 01 Aug 2010) | 1 line #6439: fix argument type for PySys_SetArgvEx() and Py_SetProgramName() in Demo/embed code. ........ r83405 | georg.brandl | 2010-08-01 16:38:17 +0200 (So, 01 Aug 2010) | 1 line #4943: do not try to include drive letters (and colons) when looking for a probably module name. ........ r83408 | georg.brandl | 2010-08-01 17:30:56 +0200 (So, 01 Aug 2010) | 1 line #5551: symbolic links never can be mount points. Fixes the fix for #1713. ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Demo/embed/Makefile python/branches/release31-maint/Demo/embed/demo.c python/branches/release31-maint/Demo/embed/loop.c python/branches/release31-maint/Lib/http/cookies.py python/branches/release31-maint/Lib/posixpath.py python/branches/release31-maint/Lib/test/test_http_cookies.py python/branches/release31-maint/Lib/timeit.py python/branches/release31-maint/Lib/trace.py python/branches/release31-maint/Misc/NEWS Modified: python/branches/release31-maint/Demo/embed/Makefile ============================================================================== --- python/branches/release31-maint/Demo/embed/Makefile (original) +++ python/branches/release31-maint/Demo/embed/Makefile Sun Aug 1 20:56:30 2010 @@ -22,7 +22,7 @@ LIBPYTHON= $(blddir)/libpython$(VERSION).a # XXX edit LIBS (in particular) to match $(blddir)/Modules/Makefile -LIBS= -lnsl -ldl -lreadline -ltermcap -lieee -lpthread -lutil +LIBS= -lnsl -ldl -lreadline -lieee -lpthread -lutil LDFLAGS= -Xlinker -export-dynamic SYSLIBS= -lm MODLIBS= Modified: python/branches/release31-maint/Demo/embed/demo.c ============================================================================== --- python/branches/release31-maint/Demo/embed/demo.c (original) +++ python/branches/release31-maint/Demo/embed/demo.c Sun Aug 1 20:56:30 2010 @@ -22,8 +22,17 @@ /* Define sys.argv. It is up to the application if you want this; you can also let it undefined (since the Python code is generally not a main program it has no business - touching sys.argv...) */ - PySys_SetArgv(2, args); + touching sys.argv...) + + If the third argument is true, sys.path is modified to include + either the directory containing the script named by argv[0], or + the current working directory. This can be risky; if you run + an application embedding Python in a directory controlled by + someone else, attackers could put a Trojan-horse module in the + directory (say, a file named os.py) that your application would + then import and run. + */ + PySys_SetArgvEx(2, args, 0); /* Do some application specific code */ printf("Hello, brave new world\n\n"); Modified: python/branches/release31-maint/Demo/embed/loop.c ============================================================================== --- python/branches/release31-maint/Demo/embed/loop.c (original) +++ python/branches/release31-maint/Demo/embed/loop.c Sun Aug 1 20:56:30 2010 @@ -19,7 +19,7 @@ count = atoi(argv[2]); } - Py_SetProgramName(argv[0]); + Py_SetProgramName(L"loop"); /* uncomment this if you don't want to load site.py */ /* Py_NoSiteFlag = 1; */ Modified: python/branches/release31-maint/Lib/http/cookies.py ============================================================================== --- python/branches/release31-maint/Lib/http/cookies.py (original) +++ python/branches/release31-maint/Lib/http/cookies.py Sun Aug 1 20:56:30 2010 @@ -439,19 +439,21 @@ # _LegalCharsPatt = r"[\w\d!#%&'~_`><@,:/\$\*\+\-\.\^\|\)\(\?\}\{\=]" -_CookiePattern = re.compile( - r"(?x)" # This is a Verbose pattern - r"(?P" # Start of group 'key' - ""+ _LegalCharsPatt +"+?" # Any word of at least one letter, nongreedy - r")" # End of group 'key' - r"\s*=\s*" # Equal Sign - r"(?P" # Start of group 'val' - r'"(?:[^\\"]|\\.)*"' # Any doublequoted string - r"|" # or - ""+ _LegalCharsPatt +"*" # Any word or empty string - r")" # End of group 'val' - r"\s*;?" # Probably ending in a semi-colon - , re.ASCII) # May be removed if safe. +_CookiePattern = re.compile(r""" + (?x) # This is a verbose pattern + (?P # Start of group 'key' + """ + _LegalCharsPatt + r"""+? # Any word of at least one letter + ) # End of group 'key' + \s*=\s* # Equal Sign + (?P # Start of group 'val' + "(?:[^\\"]|\\.)*" # Any doublequoted string + | # or + \w{3},\s[\w\d-]{9,11}\s[\d:]{8}\sGMT # Special case for "expires" attr + | # or + """ + _LegalCharsPatt + r"""* # Any word or empty string + ) # End of group 'val' + \s*;? # Probably ending in a semi-colon + """, re.ASCII) # May be removed if safe. # At long last, here is the cookie class. Modified: python/branches/release31-maint/Lib/posixpath.py ============================================================================== --- python/branches/release31-maint/Lib/posixpath.py (original) +++ python/branches/release31-maint/Lib/posixpath.py Sun Aug 1 20:56:30 2010 @@ -197,6 +197,9 @@ def ismount(path): """Test whether a path is a mount point""" + if islink(path): + # A symlink can never be a mount point + return False try: s1 = os.lstat(path) if isinstance(path, bytes): Modified: python/branches/release31-maint/Lib/test/test_http_cookies.py ============================================================================== --- python/branches/release31-maint/Lib/test/test_http_cookies.py (original) +++ python/branches/release31-maint/Lib/test/test_http_cookies.py Sun Aug 1 20:56:30 2010 @@ -65,6 +65,36 @@ """) + def test_special_attrs(self): + # 'expires' + C = cookies.SimpleCookie('Customer="WILE_E_COYOTE"') + C['Customer']['expires'] = 0 + # can't test exact output, it always depends on current date/time + self.assertTrue(C.output().endswith('GMT')) + + # loading 'expires' + C = cookies.SimpleCookie() + C.load('Customer="W"; expires=Wed, 01-Jan-2010 00:00:00 GMT') + self.assertEqual(C['Customer']['expires'], + 'Wed, 01-Jan-2010 00:00:00 GMT') + C = cookies.SimpleCookie() + C.load('Customer="W"; expires=Wed, 01-Jan-98 00:00:00 GMT') + self.assertEqual(C['Customer']['expires'], + 'Wed, 01-Jan-98 00:00:00 GMT') + + # 'max-age' + C = cookies.SimpleCookie('Customer="WILE_E_COYOTE"') + C['Customer']['max-age'] = 10 + self.assertEqual(C.output(), + 'Set-Cookie: Customer="WILE_E_COYOTE"; Max-Age=10') + + # others + C = cookies.SimpleCookie('Customer="WILE_E_COYOTE"') + C['Customer']['secure'] = True + C['Customer']['httponly'] = True + self.assertEqual(C.output(), + 'Set-Cookie: Customer="WILE_E_COYOTE"; httponly; secure') + def test_quoted_meta(self): # Try cookie with quoted meta-data C = cookies.SimpleCookie() Modified: python/branches/release31-maint/Lib/timeit.py ============================================================================== --- python/branches/release31-maint/Lib/timeit.py (original) +++ python/branches/release31-maint/Lib/timeit.py Sun Aug 1 20:56:30 2010 @@ -9,7 +9,7 @@ Library usage: see the Timer class. Command line usage: - python timeit.py [-n N] [-r N] [-s S] [-t] [-c] [-h] [statement] + python timeit.py [-n N] [-r N] [-s S] [-t] [-c] [-h] [--] [statement] Options: -n/--number N: how many times to execute 'statement' (default: see below) @@ -19,6 +19,7 @@ -c/--clock: use time.clock() (default on Windows) -v/--verbose: print raw timing results; repeat for more digits precision -h/--help: print this usage message and exit + --: separate options from statement, use when statement starts with - statement: statement to be timed (default 'pass') A multi-line statement may be given by specifying each line as a Modified: python/branches/release31-maint/Lib/trace.py ============================================================================== --- python/branches/release31-maint/Lib/trace.py (original) +++ python/branches/release31-maint/Lib/trace.py Sun Aug 1 20:56:30 2010 @@ -192,11 +192,13 @@ base = path[len(longest) + 1:] else: base = path + # the drive letter is never part of the module name + drive, base = os.path.splitdrive(base) base = base.replace(os.sep, ".") if os.altsep: base = base.replace(os.altsep, ".") filename, ext = os.path.splitext(base) - return filename + return filename.lstrip(".") class CoverageResults: def __init__(self, counts=None, calledfuncs=None, infile=None, @@ -799,7 +801,14 @@ try: with open(progname) as fp: code = compile(fp.read(), progname, 'exec') - t.run(code) + # try to emulate __main__ namespace as much as possible + globs = { + '__file__': progname, + '__name__': '__main__', + '__package__': None, + '__cached__': None, + } + t.runctx(code, globs, globs) except IOError as err: _err_exit("Cannot run file %r because: %s" % (sys.argv[0], err)) except SystemExit: Modified: python/branches/release31-maint/Misc/NEWS ============================================================================== --- python/branches/release31-maint/Misc/NEWS (original) +++ python/branches/release31-maint/Misc/NEWS Sun Aug 1 20:56:30 2010 @@ -84,6 +84,13 @@ Library ------- +- Issue #1713: Fix os.path.ismount(), which returned true for symbolic links + across devices. + +- Issue #8826: Properly load old-style "expires" attribute in http.cookies. + +- Issue #1690103: Fix initial namespace for code run with trace.main(). + - Issue #9448: Fix a leak of OS resources (mutexes or semaphores) when re-initializing a buffered IO object by calling its ``__init__`` method. From python-checkins at python.org Sun Aug 1 20:59:44 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 1 Aug 2010 20:59:44 +0200 (CEST) Subject: [Python-checkins] r83423 - in python/branches/release31-maint: Misc/RPM/python-3.1.spec Objects/unicodeobject.c Message-ID: <20100801185944.E6207EE9A2@mail.python.org> Author: georg.brandl Date: Sun Aug 1 20:59:44 2010 New Revision: 83423 Log: Merged revisions 83395,83417 via svnmerge from svn+ssh://svn.python.org/python/branches/py3k ........ r83395 | georg.brandl | 2010-08-01 10:49:18 +0200 (So, 01 Aug 2010) | 1 line #8821: do not rely on Unicode strings being terminated with a \u0000, rather explicitly check range before looking for a second surrogate character. ........ r83417 | georg.brandl | 2010-08-01 20:38:26 +0200 (So, 01 Aug 2010) | 1 line #5776: fix mistakes in python specfile. (Nobody probably uses it anyway.) ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Misc/RPM/python-3.1.spec python/branches/release31-maint/Objects/unicodeobject.c Modified: python/branches/release31-maint/Misc/RPM/python-3.1.spec ============================================================================== --- python/branches/release31-maint/Misc/RPM/python-3.1.spec (original) +++ python/branches/release31-maint/Misc/RPM/python-3.1.spec Sun Aug 1 20:59:44 2010 @@ -35,7 +35,7 @@ %define name python #--start constants-- %define version 3.1.2 -%define libver 3.1 +%define libvers 3.1 #--end constants-- %define release 1pydotorg %define __prefix /usr @@ -54,7 +54,7 @@ Name: %{name}%{binsuffix} Version: %{version} Release: %{release} -Copyright: Modified CNRI Open Source License +License: PSF Group: Development/Languages Source: Python-%{version}.tar.bz2 %if %{include_docs} @@ -256,7 +256,7 @@ then ( cd $RPM_BUILD_ROOT%{__prefix}/bin; rm -f python[0-9a-zA-Z]*; mv -f python python"%{binsuffix}" ) - ( cd $RPM_BUILD_ROOT%{__prefix}/man/man1; mv python.1 python%{binsuffix}.1 ) + ( cd $RPM_BUILD_ROOT%{__prefix}/share/man/man1; mv python.1 python%{binsuffix}.1 ) ( cd $RPM_BUILD_ROOT%{__prefix}/bin; mv -f pydoc pydoc"%{binsuffix}" ) ( cd $RPM_BUILD_ROOT%{__prefix}/bin; mv -f idle idle"%{binsuffix}" ) fi @@ -341,14 +341,13 @@ %defattr(-,root,root) %doc Misc/README Misc/cheatsheet Misc/Porting %doc LICENSE Misc/ACKS Misc/HISTORY Misc/NEWS -%{__prefix}/man/man1/python%{binsuffix}.1* +%{__prefix}/share/man/man1/python%{binsuffix}.1* %attr(755,root,root) %dir %{__prefix}/include/python%{libvers} %attr(755,root,root) %dir %{__prefix}/%{libdirname}/python%{libvers}/ %{__prefix}/%{libdirname}/python%{libvers}/*.txt %{__prefix}/%{libdirname}/python%{libvers}/*.py* %{__prefix}/%{libdirname}/python%{libvers}/pdb.doc -%{__prefix}/%{libdirname}/python%{libvers}/profile.doc %{__prefix}/%{libdirname}/python%{libvers}/curses %{__prefix}/%{libdirname}/python%{libvers}/distutils %{__prefix}/%{libdirname}/python%{libvers}/encodings Modified: python/branches/release31-maint/Objects/unicodeobject.c ============================================================================== --- python/branches/release31-maint/Objects/unicodeobject.c (original) +++ python/branches/release31-maint/Objects/unicodeobject.c Sun Aug 1 20:59:44 2010 @@ -3597,7 +3597,7 @@ ch2 = *s++; size--; - if (ch2 >= 0xDC00 && ch2 <= 0xDFFF) { + if (ch2 >= 0xDC00 && ch2 <= 0xDFFF && size) { ucs = (((ch & 0x03FF) << 10) | (ch2 & 0x03FF)) + 0x00010000; *p++ = '\\'; *p++ = 'U'; @@ -3839,7 +3839,7 @@ ch2 = *s++; size--; - if (ch2 >= 0xDC00 && ch2 <= 0xDFFF) { + if (ch2 >= 0xDC00 && ch2 <= 0xDFFF && size) { ucs = (((ch & 0x03FF) << 10) | (ch2 & 0x03FF)) + 0x00010000; *p++ = '\\'; *p++ = 'U'; From python-checkins at python.org Sun Aug 1 21:02:09 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 1 Aug 2010 21:02:09 +0200 (CEST) Subject: [Python-checkins] r83424 - in python/branches/release27-maint: Doc/library/optparse.rst Lib/mailbox.py Lib/pstats.py Lib/test/test_set.py Misc/NEWS Message-ID: <20100801190209.85CD8EE984@mail.python.org> Author: georg.brandl Date: Sun Aug 1 21:02:09 2010 New Revision: 83424 Log: Merged revisions 83385-83389,83391 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83385 | georg.brandl | 2010-08-01 08:42:45 +0200 (So, 01 Aug 2010) | 1 line #8773: mailbox.py does not need to be executable. ........ r83386 | georg.brandl | 2010-08-01 08:44:46 +0200 (So, 01 Aug 2010) | 1 line #8768: name test method properly so that it gets executed. ........ r83387 | georg.brandl | 2010-08-01 08:53:28 +0200 (So, 01 Aug 2010) | 1 line #8735: better explain semantics of *values* argument for parse(). ........ r83388 | georg.brandl | 2010-08-01 09:48:43 +0200 (So, 01 Aug 2010) | 1 line #7395: fix traceback in do_add() when no stats are loaded. Apply same fix for do_sort() and do_reverse(). ........ r83389 | georg.brandl | 2010-08-01 09:57:47 +0200 (So, 01 Aug 2010) | 1 line Small improvements to pstats browser: do not crash on reading invalid file, and actually do a reload when executing "read" as intended. ........ r83391 | georg.brandl | 2010-08-01 10:10:08 +0200 (So, 01 Aug 2010) | 1 line Add another news entry. ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Doc/library/optparse.rst python/branches/release27-maint/Lib/mailbox.py (props changed) python/branches/release27-maint/Lib/pstats.py python/branches/release27-maint/Lib/test/test_set.py python/branches/release27-maint/Misc/NEWS Modified: python/branches/release27-maint/Doc/library/optparse.rst ============================================================================== --- python/branches/release27-maint/Doc/library/optparse.rst (original) +++ python/branches/release27-maint/Doc/library/optparse.rst Sun Aug 1 21:02:09 2010 @@ -1238,8 +1238,9 @@ the list of arguments to process (default: ``sys.argv[1:]``) ``values`` - object to store option arguments in (default: a new instance of - :class:`optparse.Values`) + a :class:`optparse.Values` object to store option arguments in (default: a + new instance of :class:`Values`) -- if you give an existing object, the + option defaults will not be initialized on it and the return values are Modified: python/branches/release27-maint/Lib/pstats.py ============================================================================== --- python/branches/release27-maint/Lib/pstats.py (original) +++ python/branches/release27-maint/Lib/pstats.py Sun Aug 1 21:02:09 2010 @@ -588,7 +588,10 @@ print >> self.stream, " that match it are printed." def do_add(self, line): - self.stats.add(line) + if self.stats: + self.stats.add(line) + else: + print >> self.stream, "No statistics object is loaded." return 0 def help_add(self): print >> self.stream, "Add profile info from given file to current statistics object." @@ -623,22 +626,33 @@ except IOError, args: print >> self.stream, args[1] return + except Exception as err: + print >> self.stream, err.__class__.__name__ + ':', err + return self.prompt = line + "% " elif len(self.prompt) > 2: - line = self.prompt[-2:] + line = self.prompt[:-2] + self.do_read(line) else: print >> self.stream, "No statistics object is current -- cannot reload." return 0 def help_read(self): print >> self.stream, "Read in profile data from a specified file." + print >> self.stream, "Without argument, reload the current file." def do_reverse(self, line): - self.stats.reverse_order() + if self.stats: + self.stats.reverse_order() + else: + print >> self.stream, "No statistics object is loaded." return 0 def help_reverse(self): print >> self.stream, "Reverse the sort order of the profiling report." def do_sort(self, line): + if not self.stats: + print >> self.stream, "No statistics object is loaded." + return abbrevs = self.stats.get_sort_arg_defs() if line and all((x in abbrevs) for x in line.split()): self.stats.sort_stats(*line.split()) @@ -660,11 +674,16 @@ self.generic_help() def do_strip(self, line): - self.stats.strip_dirs() - return 0 + if self.stats: + self.stats.strip_dirs() + else: + print >> self.stream, "No statistics object is loaded." def help_strip(self): print >> self.stream, "Strip leading path information from filenames in the report." + def help_help(self): + print >> self.stream, "Show help for a given command." + def postcmd(self, stop, line): if stop: return stop Modified: python/branches/release27-maint/Lib/test/test_set.py ============================================================================== --- python/branches/release27-maint/Lib/test/test_set.py (original) +++ python/branches/release27-maint/Lib/test/test_set.py Sun Aug 1 21:02:09 2010 @@ -750,7 +750,7 @@ result = self.set ^ self.set self.assertEqual(result, empty_set) - def checkempty_symmetric_difference(self): + def test_empty_symmetric_difference(self): result = self.set ^ empty_set self.assertEqual(result, self.set) Modified: python/branches/release27-maint/Misc/NEWS ============================================================================== --- python/branches/release27-maint/Misc/NEWS (original) +++ python/branches/release27-maint/Misc/NEWS Sun Aug 1 21:02:09 2010 @@ -27,6 +27,10 @@ - Issue #9448: Fix a leak of OS resources (mutexes or semaphores) when re-initializing a buffered IO object by calling its ``__init__`` method. +- Issue #7395: Fix tracebacks in pstats interactive browser. + +- Issue #8230: Fix Lib/test/sortperf.py. + - Issue #1713: Fix os.path.ismount(), which returned true for symbolic links across devices. From python-checkins at python.org Sun Aug 1 21:04:55 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 1 Aug 2010 21:04:55 +0200 (CEST) Subject: [Python-checkins] r83425 - in python/branches/release31-maint: Doc/library/optparse.rst Lib/mailbox.py Lib/pstats.py Lib/test/test_set.py Misc/NEWS Message-ID: <20100801190455.E0D0CEE9A2@mail.python.org> Author: georg.brandl Date: Sun Aug 1 21:04:55 2010 New Revision: 83425 Log: Merged revisions 83385-83389,83391 via svnmerge from svn+ssh://svn.python.org/python/branches/py3k ........ r83385 | georg.brandl | 2010-08-01 08:42:45 +0200 (So, 01 Aug 2010) | 1 line #8773: mailbox.py does not need to be executable. ........ r83386 | georg.brandl | 2010-08-01 08:44:46 +0200 (So, 01 Aug 2010) | 1 line #8768: name test method properly so that it gets executed. ........ r83387 | georg.brandl | 2010-08-01 08:53:28 +0200 (So, 01 Aug 2010) | 1 line #8735: better explain semantics of *values* argument for parse(). ........ r83388 | georg.brandl | 2010-08-01 09:48:43 +0200 (So, 01 Aug 2010) | 1 line #7395: fix traceback in do_add() when no stats are loaded. Apply same fix for do_sort() and do_reverse(). ........ r83389 | georg.brandl | 2010-08-01 09:57:47 +0200 (So, 01 Aug 2010) | 1 line Small improvements to pstats browser: do not crash on reading invalid file, and actually do a reload when executing "read" as intended. ........ r83391 | georg.brandl | 2010-08-01 10:10:08 +0200 (So, 01 Aug 2010) | 1 line Add another news entry. ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Doc/library/optparse.rst python/branches/release31-maint/Lib/mailbox.py (props changed) python/branches/release31-maint/Lib/pstats.py python/branches/release31-maint/Lib/test/test_set.py python/branches/release31-maint/Misc/NEWS Modified: python/branches/release31-maint/Doc/library/optparse.rst ============================================================================== --- python/branches/release31-maint/Doc/library/optparse.rst (original) +++ python/branches/release31-maint/Doc/library/optparse.rst Sun Aug 1 21:04:55 2010 @@ -1228,8 +1228,9 @@ the list of arguments to process (default: ``sys.argv[1:]``) ``values`` - object to store option arguments in (default: a new instance of - :class:`optparse.Values`) + a :class:`optparse.Values` object to store option arguments in (default: a + new instance of :class:`Values`) -- if you give an existing object, the + option defaults will not be initialized on it and the return values are Modified: python/branches/release31-maint/Lib/pstats.py ============================================================================== --- python/branches/release31-maint/Lib/pstats.py (original) +++ python/branches/release31-maint/Lib/pstats.py Sun Aug 1 21:04:55 2010 @@ -596,7 +596,10 @@ print(" that match it are printed.", file=self.stream) def do_add(self, line): - self.stats.add(line) + if self.stats: + self.stats.add(line) + else: + print("No statistics object is loaded.", file=self.stream) return 0 def help_add(self): print("Add profile info from given file to current statistics object.", file=self.stream) @@ -631,22 +634,33 @@ except IOError as err: print(err.args[1], file=self.stream) return + except Exception as err: + print(err.__class__.__name__ + ':', err, file=self.stream) + return self.prompt = line + "% " elif len(self.prompt) > 2: - line = self.prompt[-2:] + line = self.prompt[:-2] + self.do_read(line) else: print("No statistics object is current -- cannot reload.", file=self.stream) return 0 def help_read(self): print("Read in profile data from a specified file.", file=self.stream) + print("Without argument, reload the current file.", file=self.stream) def do_reverse(self, line): - self.stats.reverse_order() + if self.stats: + self.stats.reverse_order() + else: + print("No statistics object is loaded.", file=self.stream) return 0 def help_reverse(self): print("Reverse the sort order of the profiling report.", file=self.stream) def do_sort(self, line): + if not self.stats: + print("No statistics object is loaded.", file=self.stream) + return abbrevs = self.stats.get_sort_arg_defs() if line and not filter(lambda x,a=abbrevs: x not in a,line.split()): self.stats.sort_stats(*line.split()) @@ -668,11 +682,16 @@ self.generic_help() def do_strip(self, line): - self.stats.strip_dirs() - return 0 + if self.stats: + self.stats.strip_dirs() + else: + print("No statistics object is loaded.", file=self.stream) def help_strip(self): print("Strip leading path information from filenames in the report.", file=self.stream) + def help_help(self): + print("Show help for a given command.", file=self.stream) + def postcmd(self, stop, line): if stop: return stop Modified: python/branches/release31-maint/Lib/test/test_set.py ============================================================================== --- python/branches/release31-maint/Lib/test/test_set.py (original) +++ python/branches/release31-maint/Lib/test/test_set.py Sun Aug 1 21:04:55 2010 @@ -797,7 +797,7 @@ result = self.set ^ self.set self.assertEqual(result, empty_set) - def checkempty_symmetric_difference(self): + def test_empty_symmetric_difference(self): result = self.set ^ empty_set self.assertEqual(result, self.set) Modified: python/branches/release31-maint/Misc/NEWS ============================================================================== --- python/branches/release31-maint/Misc/NEWS (original) +++ python/branches/release31-maint/Misc/NEWS Sun Aug 1 21:04:55 2010 @@ -84,6 +84,8 @@ Library ------- +- Issue #7395: Fix tracebacks in pstats interactive browser. + - Issue #1713: Fix os.path.ismount(), which returned true for symbolic links across devices. From python-checkins at python.org Sun Aug 1 21:06:51 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 1 Aug 2010 21:06:51 +0200 (CEST) Subject: [Python-checkins] r83426 - in python/branches/release27-maint: Lib/_MozillaCookieJar.py Lib/imaplib.py Lib/pydoc.py Makefile.pre.in Misc/NEWS Message-ID: <20100801190651.BBD06C326@mail.python.org> Author: georg.brandl Date: Sun Aug 1 21:06:51 2010 New Revision: 83426 Log: Merged revisions 83370,83372-83374,83384 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83370 | georg.brandl | 2010-07-31 23:51:48 +0200 (Sa, 31 Jul 2010) | 5 lines #8198: the Helper class should not save the stdin and stdout objects at import time, rather by default use the current streams like the other APIs that output help. ........ r83372 | georg.brandl | 2010-08-01 00:05:54 +0200 (So, 01 Aug 2010) | 1 line #4007: remove *.a and *.so.X.Y files in "make clean". ........ r83373 | georg.brandl | 2010-08-01 00:11:11 +0200 (So, 01 Aug 2010) | 1 line #5147: revert accidental indentation of header constant for MozillaCookieJar. ........ r83374 | georg.brandl | 2010-08-01 00:32:52 +0200 (So, 01 Aug 2010) | 1 line #5146: handle UID THREAD command correctly. ........ r83384 | georg.brandl | 2010-08-01 08:32:55 +0200 (So, 01 Aug 2010) | 1 line Build properties using lambdas. This makes test_pyclbr pass again, because it does not think that input and output are methods anymore. ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Lib/_MozillaCookieJar.py python/branches/release27-maint/Lib/imaplib.py python/branches/release27-maint/Lib/pydoc.py python/branches/release27-maint/Makefile.pre.in python/branches/release27-maint/Misc/NEWS Modified: python/branches/release27-maint/Lib/_MozillaCookieJar.py ============================================================================== --- python/branches/release27-maint/Lib/_MozillaCookieJar.py (original) +++ python/branches/release27-maint/Lib/_MozillaCookieJar.py Sun Aug 1 21:06:51 2010 @@ -38,9 +38,9 @@ """ magic_re = "#( Netscape)? HTTP Cookie File" header = """\ - # Netscape HTTP Cookie File - # http://www.netscape.com/newsref/std/cookie_spec.html - # This is a generated file! Do not edit. +# Netscape HTTP Cookie File +# http://www.netscape.com/newsref/std/cookie_spec.html +# This is a generated file! Do not edit. """ Modified: python/branches/release27-maint/Lib/imaplib.py ============================================================================== --- python/branches/release27-maint/Lib/imaplib.py (original) +++ python/branches/release27-maint/Lib/imaplib.py Sun Aug 1 21:06:51 2010 @@ -751,7 +751,7 @@ ', '.join(Commands[command]))) name = 'UID' typ, dat = self._simple_command(name, command, *args) - if command in ('SEARCH', 'SORT'): + if command in ('SEARCH', 'SORT', 'THREAD'): name = command else: name = 'FETCH' Modified: python/branches/release27-maint/Lib/pydoc.py ============================================================================== --- python/branches/release27-maint/Lib/pydoc.py (original) +++ python/branches/release27-maint/Lib/pydoc.py Sun Aug 1 21:06:51 2010 @@ -1705,9 +1705,12 @@ 'CONTEXTMANAGERS': ('context-managers', 'with'), } - def __init__(self, input, output): - self.input = input - self.output = output + def __init__(self, input=None, output=None): + self._input = input + self._output = output + + input = property(lambda self: self._input or sys.stdin) + output = property(lambda self: self._output or sys.stdout) def __repr__(self): if inspect.stack()[1][3] == '?': @@ -1884,7 +1887,7 @@ for modules whose descriptions contain the word "spam". ''') -help = Helper(sys.stdin, sys.stdout) +help = Helper() class Scanner: """A generic tree iterator.""" Modified: python/branches/release27-maint/Makefile.pre.in ============================================================================== --- python/branches/release27-maint/Makefile.pre.in (original) +++ python/branches/release27-maint/Makefile.pre.in Sun Aug 1 21:06:51 2010 @@ -1171,8 +1171,9 @@ find $(srcdir) -name '*.py[co]' -exec rm -f {} ';' clean: pycremoval - find . -name '*.o' -exec rm -f {} ';' + find . -name '*.[oa]' -exec rm -f {} ';' find . -name '*.s[ol]' -exec rm -f {} ';' + find . -name '*.so.[0-9]*.[0-9]*' -exec rm -f {} ';' find build -name 'fficonfig.h' -exec rm -f {} ';' || true find build -name 'fficonfig.py' -exec rm -f {} ';' || true -rm -f Lib/lib2to3/*Grammar*.pickle Modified: python/branches/release27-maint/Misc/NEWS ============================================================================== --- python/branches/release27-maint/Misc/NEWS (original) +++ python/branches/release27-maint/Misc/NEWS Sun Aug 1 21:06:51 2010 @@ -27,6 +27,14 @@ - Issue #9448: Fix a leak of OS resources (mutexes or semaphores) when re-initializing a buffered IO object by calling its ``__init__`` method. +- Issue #5146: Handle UID THREAD command correctly in imaplib. + +- Issue #5147: Fix the header generated for cookie files written by + http.cookiejar.MozillaCookieJar. + +- Issue #8198: In pydoc, output all help text to the correct stream + when sys.stdout is reassigned. + - Issue #7395: Fix tracebacks in pstats interactive browser. - Issue #8230: Fix Lib/test/sortperf.py. From python-checkins at python.org Sun Aug 1 21:07:28 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 1 Aug 2010 21:07:28 +0200 (CEST) Subject: [Python-checkins] r83427 - in python/branches/release31-maint: Lib/distutils/command/sdist.py Lib/http/server.py Lib/platform.py Lib/test/sortperf.py Misc/NEWS Message-ID: <20100801190728.86ACDEE995@mail.python.org> Author: georg.brandl Date: Sun Aug 1 21:07:28 2010 New Revision: 83427 Log: Merged revisions 83371,83390 via svnmerge from svn+ssh://svn.python.org/python/branches/py3k ........ r83371 | georg.brandl | 2010-07-31 23:54:24 +0200 (Sa, 31 Jul 2010) | 1 line #8292: Fix three instances of truth tests on return values of filter() (which is always true in Python 3). ........ r83390 | georg.brandl | 2010-08-01 10:07:49 +0200 (So, 01 Aug 2010) | 1 line #8230: make Lib/test/sortperf.py run on Python 3. ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Lib/distutils/command/sdist.py python/branches/release31-maint/Lib/http/server.py python/branches/release31-maint/Lib/platform.py python/branches/release31-maint/Lib/test/sortperf.py python/branches/release31-maint/Misc/NEWS Modified: python/branches/release31-maint/Lib/distutils/command/sdist.py ============================================================================== --- python/branches/release31-maint/Lib/distutils/command/sdist.py (original) +++ python/branches/release31-maint/Lib/distutils/command/sdist.py Sun Aug 1 21:07:28 2010 @@ -240,8 +240,7 @@ optional = ['test/test*.py', 'setup.cfg'] for pattern in optional: files = filter(os.path.isfile, glob(pattern)) - if files: - self.filelist.extend(files) + self.filelist.extend(files) # build_py is used to get: # - python modules Modified: python/branches/release31-maint/Lib/http/server.py ============================================================================== --- python/branches/release31-maint/Lib/http/server.py (original) +++ python/branches/release31-maint/Lib/http/server.py Sun Aug 1 21:07:28 2010 @@ -1015,8 +1015,9 @@ if ua: env['HTTP_USER_AGENT'] = ua co = filter(None, self.headers.get_all('cookie', [])) - if co: - env['HTTP_COOKIE'] = ', '.join(co) + cookie_str = ', '.join(co) + if cookie_str: + env['HTTP_COOKIE'] = cookie_str # XXX Other HTTP_* headers # Since we're setting the env in the parent, provide empty # values to override previously set values Modified: python/branches/release31-maint/Lib/platform.py ============================================================================== --- python/branches/release31-maint/Lib/platform.py (original) +++ python/branches/release31-maint/Lib/platform.py Sun Aug 1 21:07:28 2010 @@ -1109,7 +1109,7 @@ except AttributeError: no_os_uname = 1 - if no_os_uname or not filter(None, (system, node, release, version, machine)): + if no_os_uname or not list(filter(None, (system, node, release, version, machine))): # Hmm, no there is either no uname or uname has returned #'unknowns'... we'll have to poke around the system then. if no_os_uname: Modified: python/branches/release31-maint/Lib/test/sortperf.py ============================================================================== --- python/branches/release31-maint/Lib/test/sortperf.py (original) +++ python/branches/release31-maint/Lib/test/sortperf.py Sun Aug 1 21:07:28 2010 @@ -118,12 +118,12 @@ L = L * (n // 4) # Force the elements to be distinct objects, else timings can be # artificially low. - L = map(lambda x: --x, L) + L = list(map(lambda x: --x, L)) doit(L) # ~sort del L # All equal. Again, force the elements to be distinct objects. - L = map(abs, [-0.5] * n) + L = list(map(abs, [-0.5] * n)) doit(L) # =sort del L @@ -131,11 +131,11 @@ # for an older implementation of quicksort, which used the median # of the first, last and middle elements as the pivot. half = n // 2 - L = range(half - 1, -1, -1) + L = list(range(half - 1, -1, -1)) L.extend(range(half)) # Force to float, so that the timings are comparable. This is # significantly faster if we leave tham as ints. - L = map(float, L) + L = list(map(float, L)) doit(L) # !sort print() Modified: python/branches/release31-maint/Misc/NEWS ============================================================================== --- python/branches/release31-maint/Misc/NEWS (original) +++ python/branches/release31-maint/Misc/NEWS Sun Aug 1 21:07:28 2010 @@ -84,6 +84,8 @@ Library ------- +- Issue #8230: Fix Lib/test/sortperf.py. + - Issue #7395: Fix tracebacks in pstats interactive browser. - Issue #1713: Fix os.path.ismount(), which returned true for symbolic links From python-checkins at python.org Sun Aug 1 21:09:08 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 1 Aug 2010 21:09:08 +0200 (CEST) Subject: [Python-checkins] r83428 - in python/branches/release31-maint: Lib/http/cookiejar.py Lib/imaplib.py Lib/pydoc.py Makefile.pre.in Misc/NEWS Message-ID: <20100801190908.003E8EE987@mail.python.org> Author: georg.brandl Date: Sun Aug 1 21:09:07 2010 New Revision: 83428 Log: Merged revisions 83370,83372-83374,83384 via svnmerge from svn+ssh://svn.python.org/python/branches/py3k ........ r83370 | georg.brandl | 2010-07-31 23:51:48 +0200 (Sa, 31 Jul 2010) | 5 lines #8198: the Helper class should not save the stdin and stdout objects at import time, rather by default use the current streams like the other APIs that output help. ........ r83372 | georg.brandl | 2010-08-01 00:05:54 +0200 (So, 01 Aug 2010) | 1 line #4007: remove *.a and *.so.X.Y files in "make clean". ........ r83373 | georg.brandl | 2010-08-01 00:11:11 +0200 (So, 01 Aug 2010) | 1 line #5147: revert accidental indentation of header constant for MozillaCookieJar. ........ r83374 | georg.brandl | 2010-08-01 00:32:52 +0200 (So, 01 Aug 2010) | 1 line #5146: handle UID THREAD command correctly. ........ r83384 | georg.brandl | 2010-08-01 08:32:55 +0200 (So, 01 Aug 2010) | 1 line Build properties using lambdas. This makes test_pyclbr pass again, because it does not think that input and output are methods anymore. ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Lib/http/cookiejar.py python/branches/release31-maint/Lib/imaplib.py python/branches/release31-maint/Lib/pydoc.py python/branches/release31-maint/Makefile.pre.in python/branches/release31-maint/Misc/NEWS Modified: python/branches/release31-maint/Lib/http/cookiejar.py ============================================================================== --- python/branches/release31-maint/Lib/http/cookiejar.py (original) +++ python/branches/release31-maint/Lib/http/cookiejar.py Sun Aug 1 21:09:07 2010 @@ -1964,9 +1964,9 @@ """ magic_re = re.compile("#( Netscape)? HTTP Cookie File") header = """\ - # Netscape HTTP Cookie File - # http://www.netscape.com/newsref/std/cookie_spec.html - # This is a generated file! Do not edit. +# Netscape HTTP Cookie File +# http://www.netscape.com/newsref/std/cookie_spec.html +# This is a generated file! Do not edit. """ Modified: python/branches/release31-maint/Lib/imaplib.py ============================================================================== --- python/branches/release31-maint/Lib/imaplib.py (original) +++ python/branches/release31-maint/Lib/imaplib.py Sun Aug 1 21:09:07 2010 @@ -765,7 +765,7 @@ ', '.join(Commands[command]))) name = 'UID' typ, dat = self._simple_command(name, command, *args) - if command in ('SEARCH', 'SORT'): + if command in ('SEARCH', 'SORT', 'THREAD'): name = command else: name = 'FETCH' Modified: python/branches/release31-maint/Lib/pydoc.py ============================================================================== --- python/branches/release31-maint/Lib/pydoc.py (original) +++ python/branches/release31-maint/Lib/pydoc.py Sun Aug 1 21:09:07 2010 @@ -1694,9 +1694,12 @@ 'CONTEXTMANAGERS': ('context-managers', 'with'), } - def __init__(self, input, output): - self.input = input - self.output = output + def __init__(self, input=None, output=None): + self._input = input + self._output = output + + input = property(lambda self: self._input or sys.stdin) + output = property(lambda self: self._output or sys.stdout) def __repr__(self): if inspect.stack()[1][3] == '?': @@ -1872,7 +1875,7 @@ for modules whose descriptions contain the word "spam". ''') -help = Helper(sys.stdin, sys.stdout) +help = Helper() class Scanner: """A generic tree iterator.""" Modified: python/branches/release31-maint/Makefile.pre.in ============================================================================== --- python/branches/release31-maint/Makefile.pre.in (original) +++ python/branches/release31-maint/Makefile.pre.in Sun Aug 1 21:09:07 2010 @@ -1166,8 +1166,9 @@ -rm -rf Doc/tools/sphinx Doc/tools/pygments Doc/tools/docutils clean: pycremoval - find . -name '*.o' -exec rm -f {} ';' + find . -name '*.[oa]' -exec rm -f {} ';' find . -name '*.s[ol]' -exec rm -f {} ';' + find . -name '*.so.[0-9]*.[0-9]*' -exec rm -f {} ';' find build -name 'fficonfig.h' -exec rm -f {} ';' || true find build -name 'fficonfig.py' -exec rm -f {} ';' || true -rm -f Lib/lib2to3/*Grammar*.pickle Modified: python/branches/release31-maint/Misc/NEWS ============================================================================== --- python/branches/release31-maint/Misc/NEWS (original) +++ python/branches/release31-maint/Misc/NEWS Sun Aug 1 21:09:07 2010 @@ -84,6 +84,14 @@ Library ------- +- Issue #5146: Handle UID THREAD command correctly in imaplib. + +- Issue #5147: Fix the header generated for cookie files written by + http.cookiejar.MozillaCookieJar. + +- Issue #8198: In pydoc, output all help text to the correct stream + when sys.stdout is reassigned. + - Issue #8230: Fix Lib/test/sortperf.py. - Issue #7395: Fix tracebacks in pstats interactive browser. From python-checkins at python.org Sun Aug 1 21:14:56 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 1 Aug 2010 21:14:56 +0200 (CEST) Subject: [Python-checkins] r83429 - in python/branches/release27-maint: Doc/library/sys.rst Lib/ntpath.py Lib/test/data/README Lib/test/test_ntpath.py Lib/test/test_optparse.py Misc/NEWS Python/getversion.c Message-ID: <20100801191456.93589EE9D5@mail.python.org> Author: georg.brandl Date: Sun Aug 1 21:14:56 2010 New Revision: 83429 Log: Merged revisions 83352,83356-83358,83362,83366,83368-83369 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83352 | georg.brandl | 2010-07-31 20:11:07 +0200 (Sa, 31 Jul 2010) | 1 line #9440: Remove borderline test case that fails based on unpredictable conditions such as compiler flags. ........ r83356 | georg.brandl | 2010-07-31 21:29:15 +0200 (Sa, 31 Jul 2010) | 1 line Remove trailing whitespace. ........ r83357 | georg.brandl | 2010-07-31 21:59:55 +0200 (Sa, 31 Jul 2010) | 1 line #5778: document that sys.version can contain a newline. ........ r83358 | georg.brandl | 2010-07-31 22:05:31 +0200 (Sa, 31 Jul 2010) | 1 line #9442: do not document a specific format for sys.version; rather refer to version_info and the platform module. ........ r83362 | georg.brandl | 2010-07-31 23:12:15 +0200 (Sa, 31 Jul 2010) | 1 line #8910: add a file explaining why Lib/test/data is there. ........ r83366 | georg.brandl | 2010-07-31 23:26:40 +0200 (Sa, 31 Jul 2010) | 1 line There always is a False and True now. ........ r83368 | georg.brandl | 2010-07-31 23:40:15 +0200 (Sa, 31 Jul 2010) | 1 line #7909: the prefixes \\.\ and \\?\ indicate special Windows paths, do not try to manipulate them. See http://msdn.microsoft.com/en-us/library/aa365247%28VS.85%29.aspx for details. ........ r83369 | georg.brandl | 2010-07-31 23:41:42 +0200 (Sa, 31 Jul 2010) | 1 line Fix "Berkeley" name. ........ Added: python/branches/release27-maint/Lib/test/data/README - copied unchanged from r83369, /python/branches/py3k/Lib/test/data/README Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Doc/library/sys.rst python/branches/release27-maint/Lib/ntpath.py python/branches/release27-maint/Lib/test/test_ntpath.py python/branches/release27-maint/Lib/test/test_optparse.py python/branches/release27-maint/Misc/NEWS python/branches/release27-maint/Python/getversion.c Modified: python/branches/release27-maint/Doc/library/sys.rst ============================================================================== --- python/branches/release27-maint/Doc/library/sys.rst (original) +++ python/branches/release27-maint/Doc/library/sys.rst Sun Aug 1 21:14:56 2010 @@ -967,14 +967,10 @@ .. data:: version A string containing the version number of the Python interpreter plus additional - information on the build number and compiler used. It has a value of the form - ``'version (#build_number, build_date, build_time) [compiler]'``. The first - three characters are used to identify the version in the installation - directories (where appropriate on each platform). An example:: - - >>> import sys - >>> sys.version - '1.5.2 (#0 Apr 13 1999, 10:51:12) [MSC 32 bit (Intel)]' + information on the build number and compiler used. This string is displayed + when the interactive interpreter is started. Do not extract version information + out of it, rather, use :data:`version_info` and the functions provided by the + :mod:`platform` module. .. data:: api_version Modified: python/branches/release27-maint/Lib/ntpath.py ============================================================================== --- python/branches/release27-maint/Lib/ntpath.py (original) +++ python/branches/release27-maint/Lib/ntpath.py Sun Aug 1 21:14:56 2010 @@ -399,6 +399,12 @@ """Normalize path, eliminating double slashes, etc.""" # Preserve unicode (if path is unicode) backslash, dot = (u'\\', u'.') if isinstance(path, unicode) else ('\\', '.') + if path.startswith(('\\\\.\\', '\\\\?\\')): + # in the case of paths with these prefixes: + # \\.\ -> device names + # \\?\ -> literal paths + # do not do any normalization, but return the path unchanged + return path path = path.replace("/", "\\") prefix, path = splitdrive(path) # We need to be careful here. If the prefix is empty, and the path starts Modified: python/branches/release27-maint/Lib/test/test_ntpath.py ============================================================================== --- python/branches/release27-maint/Lib/test/test_ntpath.py (original) +++ python/branches/release27-maint/Lib/test/test_ntpath.py Sun Aug 1 21:14:56 2010 @@ -123,6 +123,9 @@ tester("ntpath.normpath('C:////a/b')", r'C:\a\b') tester("ntpath.normpath('//machine/share//a/b')", r'\\machine\share\a\b') + tester("ntpath.normpath('\\\\.\\NUL')", r'\\.\NUL') + tester("ntpath.normpath('\\\\?\\D:/XY\\Z')", r'\\?\D:/XY\Z') + def test_expandvars(self): with test_support.EnvironmentVarGuard() as env: env.clear() Modified: python/branches/release27-maint/Lib/test/test_optparse.py ============================================================================== --- python/branches/release27-maint/Lib/test/test_optparse.py (original) +++ python/branches/release27-maint/Lib/test/test_optparse.py Sun Aug 1 21:14:56 2010 @@ -791,15 +791,13 @@ (options, args) = self.assertParseOK(["-q"], {'verbose': 0}, []) - if hasattr(__builtins__, 'False'): - self.assertTrue(options.verbose is False) + self.assertTrue(options.verbose is False) def test_bool_true(self): (options, args) = self.assertParseOK(["-v"], {'verbose': 1}, []) - if hasattr(__builtins__, 'True'): - self.assertTrue(options.verbose is True) + self.assertTrue(options.verbose is True) def test_bool_flicker_on_and_off(self): self.assertParseOK(["-qvq", "-q", "-v"], Modified: python/branches/release27-maint/Misc/NEWS ============================================================================== --- python/branches/release27-maint/Misc/NEWS (original) +++ python/branches/release27-maint/Misc/NEWS Sun Aug 1 21:14:56 2010 @@ -27,6 +27,9 @@ - Issue #9448: Fix a leak of OS resources (mutexes or semaphores) when re-initializing a buffered IO object by calling its ``__init__`` method. +- Issue #7909: Do not touch paths with the special prefixes ``\\.\`` + or ``\\?\`` in ntpath.normpath(). + - Issue #5146: Handle UID THREAD command correctly in imaplib. - Issue #5147: Fix the header generated for cookie files written by Modified: python/branches/release27-maint/Python/getversion.c ============================================================================== --- python/branches/release27-maint/Python/getversion.c (original) +++ python/branches/release27-maint/Python/getversion.c Sun Aug 1 21:14:56 2010 @@ -9,7 +9,7 @@ Py_GetVersion(void) { static char version[250]; - PyOS_snprintf(version, sizeof(version), "%.80s (%.80s) %.80s", + PyOS_snprintf(version, sizeof(version), "%.80s (%.80s) %.80s", PY_VERSION, Py_GetBuildInfo(), Py_GetCompiler()); return version; } From python-checkins at python.org Sun Aug 1 21:17:57 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 1 Aug 2010 21:17:57 +0200 (CEST) Subject: [Python-checkins] r83430 - in python/branches/release31-maint: Doc/library/dbm.rst Doc/library/sys.rst Lib/ntpath.py Lib/test/test_ntpath.py Lib/test/test_optparse.py Lib/test/test_tcl.py Misc/NEWS Python/getversion.c Message-ID: <20100801191757.93178EE992@mail.python.org> Author: georg.brandl Date: Sun Aug 1 21:17:57 2010 New Revision: 83430 Log: Merged revisions 83352,83355-83358,83362,83366,83368-83369 via svnmerge from svn+ssh://svn.python.org/python/branches/py3k ........ r83352 | georg.brandl | 2010-07-31 20:11:07 +0200 (Sa, 31 Jul 2010) | 1 line #9440: Remove borderline test case that fails based on unpredictable conditions such as compiler flags. ........ r83355 | georg.brandl | 2010-07-31 21:17:11 +0200 (Sa, 31 Jul 2010) | 1 line Fix bad merge: test_support -> support. ........ r83356 | georg.brandl | 2010-07-31 21:29:15 +0200 (Sa, 31 Jul 2010) | 1 line Remove trailing whitespace. ........ r83357 | georg.brandl | 2010-07-31 21:59:55 +0200 (Sa, 31 Jul 2010) | 1 line #5778: document that sys.version can contain a newline. ........ r83358 | georg.brandl | 2010-07-31 22:05:31 +0200 (Sa, 31 Jul 2010) | 1 line #9442: do not document a specific format for sys.version; rather refer to version_info and the platform module. ........ r83362 | georg.brandl | 2010-07-31 23:12:15 +0200 (Sa, 31 Jul 2010) | 1 line #8910: add a file explaining why Lib/test/data is there. ........ r83366 | georg.brandl | 2010-07-31 23:26:40 +0200 (Sa, 31 Jul 2010) | 1 line There always is a False and True now. ........ r83368 | georg.brandl | 2010-07-31 23:40:15 +0200 (Sa, 31 Jul 2010) | 1 line #7909: the prefixes \\.\ and \\?\ indicate special Windows paths, do not try to manipulate them. See http://msdn.microsoft.com/en-us/library/aa365247%28VS.85%29.aspx for details. ........ r83369 | georg.brandl | 2010-07-31 23:41:42 +0200 (Sa, 31 Jul 2010) | 1 line Fix "Berkeley" name. ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Doc/library/dbm.rst python/branches/release31-maint/Doc/library/sys.rst python/branches/release31-maint/Lib/ntpath.py python/branches/release31-maint/Lib/test/test_ntpath.py python/branches/release31-maint/Lib/test/test_optparse.py python/branches/release31-maint/Lib/test/test_tcl.py python/branches/release31-maint/Misc/NEWS python/branches/release31-maint/Python/getversion.c Modified: python/branches/release31-maint/Doc/library/dbm.rst ============================================================================== --- python/branches/release31-maint/Doc/library/dbm.rst (original) +++ python/branches/release31-maint/Doc/library/dbm.rst Sun Aug 1 21:17:57 2010 @@ -5,10 +5,10 @@ :synopsis: Interfaces to various Unix "database" formats. :mod:`dbm` is a generic interface to variants of the DBM database --- - :mod:`dbm.gnu` or :mod:`dbm.ndbm`. If none of these modules is installed, the - slow-but-simple implementation in module :mod:`dbm.dumb` will be used. There - is a `third party interface `_ to - the Oracle Berkely DB. +:mod:`dbm.gnu` or :mod:`dbm.ndbm`. If none of these modules is installed, the +slow-but-simple implementation in module :mod:`dbm.dumb` will be used. There +is a `third party interface `_ to +the Oracle Berkeley DB. .. exception:: error Modified: python/branches/release31-maint/Doc/library/sys.rst ============================================================================== --- python/branches/release31-maint/Doc/library/sys.rst (original) +++ python/branches/release31-maint/Doc/library/sys.rst Sun Aug 1 21:17:57 2010 @@ -853,14 +853,10 @@ .. data:: version A string containing the version number of the Python interpreter plus additional - information on the build number and compiler used. It has a value of the form - ``'version (#build_number, build_date, build_time) [compiler]'``. The first - three characters are used to identify the version in the installation - directories (where appropriate on each platform). An example:: - - >>> import sys - >>> sys.version - '1.5.2 (#0 Apr 13 1999, 10:51:12) [MSC 32 bit (Intel)]' + information on the build number and compiler used. This string is displayed + when the interactive interpreter is started. Do not extract version information + out of it, rather, use :data:`version_info` and the functions provided by the + :mod:`platform` module. .. data:: api_version Modified: python/branches/release31-maint/Lib/ntpath.py ============================================================================== --- python/branches/release31-maint/Lib/ntpath.py (original) +++ python/branches/release31-maint/Lib/ntpath.py Sun Aug 1 21:17:57 2010 @@ -70,6 +70,12 @@ else: return ':' +def _get_special(path): + if isinstance(path, bytes): + return (b'\\\\.\\', b'\\\\?\\') + else: + return ('\\\\.\\', '\\\\?\\') + # Normalize the case of a pathname and map slashes to backslashes. # Other normalizations (such as optimizing '../' away) are not done # (this is done by normpath). @@ -508,6 +514,13 @@ """Normalize path, eliminating double slashes, etc.""" sep = _get_sep(path) dotdot = _get_dot(path) * 2 + special_prefixes = _get_special(path) + if path.startswith(special_prefixes): + # in the case of paths with these prefixes: + # \\.\ -> device names + # \\?\ -> literal paths + # do not do any normalization, but return the path unchanged + return path path = path.replace(_get_altsep(path), sep) prefix, path = splitdrive(path) Modified: python/branches/release31-maint/Lib/test/test_ntpath.py ============================================================================== --- python/branches/release31-maint/Lib/test/test_ntpath.py (original) +++ python/branches/release31-maint/Lib/test/test_ntpath.py Sun Aug 1 21:17:57 2010 @@ -174,6 +174,9 @@ tester("ntpath.normpath('C:////a/b')", r'C:\a\b') tester("ntpath.normpath('//machine/share//a/b')", r'\\machine\share\a\b') + tester("ntpath.normpath('\\\\.\\NUL')", r'\\.\NUL') + tester("ntpath.normpath('\\\\?\\D:/XY\\Z')", r'\\?\D:/XY\Z') + def test_expandvars(self): with support.EnvironmentVarGuard() as env: env.clear() Modified: python/branches/release31-maint/Lib/test/test_optparse.py ============================================================================== --- python/branches/release31-maint/Lib/test/test_optparse.py (original) +++ python/branches/release31-maint/Lib/test/test_optparse.py Sun Aug 1 21:17:57 2010 @@ -783,15 +783,13 @@ (options, args) = self.assertParseOK(["-q"], {'verbose': 0}, []) - if hasattr(__builtins__, 'False'): - self.assertTrue(options.verbose is False) + self.assertTrue(options.verbose is False) def test_bool_true(self): (options, args) = self.assertParseOK(["-v"], {'verbose': 1}, []) - if hasattr(__builtins__, 'True'): - self.assertTrue(options.verbose is True) + self.assertTrue(options.verbose is True) def test_bool_flicker_on_and_off(self): self.assertParseOK(["-qvq", "-q", "-v"], Modified: python/branches/release31-maint/Lib/test/test_tcl.py ============================================================================== --- python/branches/release31-maint/Lib/test/test_tcl.py (original) +++ python/branches/release31-maint/Lib/test/test_tcl.py Sun Aug 1 21:17:57 2010 @@ -143,7 +143,7 @@ fullname[0], fullname[3:]) - with test_support.EnvironmentVarGuard() as env: + with support.EnvironmentVarGuard() as env: env.unset("TCL_LIBRARY") f = os.popen('%s -c "import Tkinter; print Tkinter"' % (unc_name,)) Modified: python/branches/release31-maint/Misc/NEWS ============================================================================== --- python/branches/release31-maint/Misc/NEWS (original) +++ python/branches/release31-maint/Misc/NEWS Sun Aug 1 21:17:57 2010 @@ -84,6 +84,9 @@ Library ------- +- Issue #7909: Do not touch paths with the special prefixes ``\\.\`` + or ``\\?\`` in ntpath.normpath(). + - Issue #5146: Handle UID THREAD command correctly in imaplib. - Issue #5147: Fix the header generated for cookie files written by @@ -1551,8 +1554,8 @@ an error. The _PY_STRUCT_FLOAT_COERCE constant has been removed. The version number has been bumped to 0.3. -- Issue #5359: Readd the Berkley-DB detection code to allow _dbm be built - using Berkley-DB. +- Issue #5359: Readd the Berkeley DB detection code to allow _dbm be built + using Berkeley DB. Tests ----- Modified: python/branches/release31-maint/Python/getversion.c ============================================================================== --- python/branches/release31-maint/Python/getversion.c (original) +++ python/branches/release31-maint/Python/getversion.c Sun Aug 1 21:17:57 2010 @@ -9,7 +9,7 @@ Py_GetVersion(void) { static char version[250]; - PyOS_snprintf(version, sizeof(version), "%.80s (%.80s) %.80s", + PyOS_snprintf(version, sizeof(version), "%.80s (%.80s) %.80s", PY_VERSION, Py_GetBuildInfo(), Py_GetCompiler()); return version; } From python-checkins at python.org Sun Aug 1 21:18:13 2010 From: python-checkins at python.org (ronald.oussoren) Date: Sun, 1 Aug 2010 21:18:13 +0200 (CEST) Subject: [Python-checkins] r83431 - python/branches/py3k/Lib/test/test_posix.py Message-ID: <20100801191813.312BDEEAAB@mail.python.org> Author: ronald.oussoren Date: Sun Aug 1 21:18:13 2010 New Revision: 83431 Log: test_getgroups as introduced with issue7900 failed on systems where 'id -G' and posix.getgroups() returned the same information, but one of the sources contains duplicate information. Rewrite the check using sets instead of lists. Modified: python/branches/py3k/Lib/test/test_posix.py Modified: python/branches/py3k/Lib/test/test_posix.py ============================================================================== --- python/branches/py3k/Lib/test/test_posix.py (original) +++ python/branches/py3k/Lib/test/test_posix.py Sun Aug 1 21:18:13 2010 @@ -357,11 +357,11 @@ if not groups: raise unittest.SkipTest("need working 'id -G'") - # The order of groups isn't important, hence the calls - # to sorted. + # 'id -G' and 'os.getgroups()' should return the same + # groups, ignoring order and duplicates. self.assertEqual( - list(sorted([int(x) for x in groups.split()])), - list(sorted(posix.getgroups()))) + set([int(x) for x in groups.split()]), + set(posix.getgroups())) class PosixGroupsTester(unittest.TestCase): From python-checkins at python.org Sun Aug 1 21:21:26 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 1 Aug 2010 21:21:26 +0200 (CEST) Subject: [Python-checkins] r83432 - in python/branches/release27-maint: Doc/library/collections.rst Doc/library/datetime.rst Message-ID: <20100801192126.48CD2EE992@mail.python.org> Author: georg.brandl Date: Sun Aug 1 21:21:26 2010 New Revision: 83432 Log: Merged revisions 83328,83341 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83328 | raymond.hettinger | 2010-07-31 12:14:41 +0200 (Sa, 31 Jul 2010) | 1 line Document how to change OrderedDict update order from first to last. ........ r83341 | georg.brandl | 2010-07-31 13:40:07 +0200 (Sa, 31 Jul 2010) | 1 line #9430: document timedelta str() and repr(). ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Doc/library/collections.rst python/branches/release27-maint/Doc/library/datetime.rst Modified: python/branches/release27-maint/Doc/library/collections.rst ============================================================================== --- python/branches/release27-maint/Doc/library/collections.rst (original) +++ python/branches/release27-maint/Doc/library/collections.rst Sun Aug 1 21:21:26 2010 @@ -958,3 +958,15 @@ The new sorted dictionaries maintain their sort order when entries are deleted. But when new keys are added, the keys are appended to the end and the sort is not maintained. + +It is also straight-forward to create an ordered dictionary variant +that the remembers the order the keys were *last* inserted. +If a new entry overwrites an existing entry, the +original insertion position is changed and moved to the end:: + + class LastUpdatedOrderedDict(OrderedDict): + 'Store items is the order the keys were last added' + def __setitem__(self, key, value): + if key in self: + del self[key] + OrderedDict.__setitem__(self, key, value) Modified: python/branches/release27-maint/Doc/library/datetime.rst ============================================================================== --- python/branches/release27-maint/Doc/library/datetime.rst (original) +++ python/branches/release27-maint/Doc/library/datetime.rst Sun Aug 1 21:21:26 2010 @@ -235,6 +235,14 @@ | ``abs(t)`` | equivalent to +\ *t* when ``t.days >= 0``, and| | | to -*t* when ``t.days < 0``. (2) | +--------------------------------+-----------------------------------------------+ +| ``str(t)`` | Returns a string in the form | +| | ``[D day[s], ][H]H:MM:SS[.UUUUUU]``, where D | +| | is negative for negative ``t``. (5) | ++--------------------------------+-----------------------------------------------+ +| ``repr(t)`` | Returns a string in the form | +| | ``datetime.timedelta(D[, S[, U]])``, where D | +| | is negative for negative ``t``. (5) | ++--------------------------------+-----------------------------------------------+ Notes: @@ -250,6 +258,16 @@ (4) -*timedelta.max* is not representable as a :class:`timedelta` object. +(5) + String representations of :class:`timedelta` objects are normalized + similarly to their internal representation. This leads to somewhat + unusual results for negative timedeltas. For example: + + >>> timedelta(hours=-5) + datetime.timedelta(-1, 68400) + >>> print(_) + -1 day, 19:00:00 + In addition to the operations listed above :class:`timedelta` objects support certain additions and subtractions with :class:`date` and :class:`datetime` objects (see below). From python-checkins at python.org Sun Aug 1 21:21:34 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 1 Aug 2010 21:21:34 +0200 (CEST) Subject: [Python-checkins] r83433 - in python/branches/release31-maint: Doc/library/datetime.rst Modules/_ctypes/ctypes.h Message-ID: <20100801192134.7FC38EEAAB@mail.python.org> Author: georg.brandl Date: Sun Aug 1 21:21:34 2010 New Revision: 83433 Log: Merged revisions 83325,83328,83341 via svnmerge from svn+ssh://svn.python.org/python/branches/py3k ........ r83325 | georg.brandl | 2010-07-31 11:37:03 +0200 (Sa, 31 Jul 2010) | 1 line Copy Sun-specific inclusion of from 2.7 maint to trunk; it seems to not have been merged to py3k. ........ r83328 | raymond.hettinger | 2010-07-31 12:14:41 +0200 (Sa, 31 Jul 2010) | 1 line Document how to change OrderedDict update order from first to last. ........ r83341 | georg.brandl | 2010-07-31 13:40:07 +0200 (Sa, 31 Jul 2010) | 1 line #9430: document timedelta str() and repr(). ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Doc/library/datetime.rst python/branches/release31-maint/Modules/_ctypes/ctypes.h Modified: python/branches/release31-maint/Doc/library/datetime.rst ============================================================================== --- python/branches/release31-maint/Doc/library/datetime.rst (original) +++ python/branches/release31-maint/Doc/library/datetime.rst Sun Aug 1 21:21:34 2010 @@ -233,6 +233,14 @@ | ``abs(t)`` | equivalent to +\ *t* when ``t.days >= 0``, and| | | to -*t* when ``t.days < 0``. (2) | +--------------------------------+-----------------------------------------------+ +| ``str(t)`` | Returns a string in the form | +| | ``[D day[s], ][H]H:MM:SS[.UUUUUU]``, where D | +| | is negative for negative ``t``. (5) | ++--------------------------------+-----------------------------------------------+ +| ``repr(t)`` | Returns a string in the form | +| | ``datetime.timedelta(D[, S[, U]])``, where D | +| | is negative for negative ``t``. (5) | ++--------------------------------+-----------------------------------------------+ Notes: @@ -248,6 +256,16 @@ (4) -*timedelta.max* is not representable as a :class:`timedelta` object. +(5) + String representations of :class:`timedelta` objects are normalized + similarly to their internal representation. This leads to somewhat + unusual results for negative timedeltas. For example: + + >>> timedelta(hours=-5) + datetime.timedelta(-1, 68400) + >>> print(_) + -1 day, 19:00:00 + In addition to the operations listed above :class:`timedelta` objects support certain additions and subtractions with :class:`date` and :class:`datetime` objects (see below). Modified: python/branches/release31-maint/Modules/_ctypes/ctypes.h ============================================================================== --- python/branches/release31-maint/Modules/_ctypes/ctypes.h (original) +++ python/branches/release31-maint/Modules/_ctypes/ctypes.h Sun Aug 1 21:21:34 2010 @@ -1,3 +1,7 @@ +#if defined (__SVR4) && defined (__sun) +# include +#endif + #ifndef MS_WIN32 #define max(a, b) ((a) > (b) ? (a) : (b)) #define min(a, b) ((a) < (b) ? (a) : (b)) From python-checkins at python.org Sun Aug 1 21:23:23 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 1 Aug 2010 21:23:23 +0200 (CEST) Subject: [Python-checkins] r83434 - python/branches/release27-maint Message-ID: <20100801192323.3D81CEE992@mail.python.org> Author: georg.brandl Date: Sun Aug 1 21:23:23 2010 New Revision: 83434 Log: Blocked revisions 83320-83323,83325-83327,83329,83339,83342-83343,83348-83351,83355,83359-83361,83371,83375,83390,83394,83399,83404,83406 via svnmerge ........ r83320 | georg.brandl | 2010-07-31 11:01:16 +0200 (Sa, 31 Jul 2010) | 1 line Update copyright years and add releases to release list. Also update Sphinx version number. ........ r83321 | georg.brandl | 2010-07-31 11:03:30 +0200 (Sa, 31 Jul 2010) | 1 line At least give IDLE 3.1 a release date. No further entries there for 3.2 though. ........ r83322 | tarek.ziade | 2010-07-31 11:10:51 +0200 (Sa, 31 Jul 2010) | 1 line reverted distutils doc to its 3.1 state ........ r83323 | georg.brandl | 2010-07-31 11:15:10 +0200 (Sa, 31 Jul 2010) | 1 line After distutils doc reversal, change back **bold todo** items to XXX comments. ........ r83325 | georg.brandl | 2010-07-31 11:37:03 +0200 (Sa, 31 Jul 2010) | 1 line Copy Sun-specific inclusion of from 2.7 maint to trunk; it seems to not have been merged to py3k. ........ r83326 | georg.brandl | 2010-07-31 12:08:09 +0200 (Sa, 31 Jul 2010) | 1 line Avoid triggering DeprecationWarnings in test_smtpd and smtpd. ........ r83327 | raymond.hettinger | 2010-07-31 12:11:39 +0200 (Sa, 31 Jul 2010) | 1 line Add functools.lfu_cache() and functools.lru_cache(). ........ r83329 | georg.brandl | 2010-07-31 12:16:21 +0200 (Sa, 31 Jul 2010) | 1 line Revert r83327. This will have to wait until after the alpha1 release. ........ r83339 | georg.brandl | 2010-07-31 13:00:47 +0200 (Sa, 31 Jul 2010) | 1 line Rewrap. ........ r83342 | georg.brandl | 2010-07-31 13:52:46 +0200 (Sa, 31 Jul 2010) | 1 line Import test_pdb with its full name, so that running python -m test.test_pdb succeeds. ........ r83343 | georg.brandl | 2010-07-31 14:06:51 +0200 (Sa, 31 Jul 2010) | 1 line From Martin: New UUIDs for the 3.2 release series. ........ r83348 | georg.brandl | 2010-07-31 20:05:35 +0200 (Sa, 31 Jul 2010) | 1 line Post-release updates. ........ r83349 | antoine.pitrou | 2010-07-31 20:08:33 +0200 (Sa, 31 Jul 2010) | 3 lines Add ssl changes to the 3.2 "what's new". ........ r83350 | georg.brandl | 2010-07-31 20:09:23 +0200 (Sa, 31 Jul 2010) | 1 line Re-commit r83327 now that the release is done. ........ r83351 | georg.brandl | 2010-07-31 20:09:46 +0200 (Sa, 31 Jul 2010) | 1 line Move news item to the correct position. ........ r83355 | georg.brandl | 2010-07-31 21:17:11 +0200 (Sa, 31 Jul 2010) | 1 line Fix bad merge: test_support -> support. ........ r83359 | georg.brandl | 2010-07-31 22:08:15 +0200 (Sa, 31 Jul 2010) | 1 line #1286: allow using fileinput.FileInput as context manager. ........ r83360 | georg.brandl | 2010-07-31 22:13:44 +0200 (Sa, 31 Jul 2010) | 1 line Clarify comment in comments test case explaining comment semantics. ........ r83361 | georg.brandl | 2010-07-31 23:04:00 +0200 (Sa, 31 Jul 2010) | 1 line #3788: more tests for http.cookies, now at 95% coverage. Also bring coding style in the module up to PEP 8, where it does not break backwards compatibility. ........ r83371 | georg.brandl | 2010-07-31 23:54:24 +0200 (Sa, 31 Jul 2010) | 1 line #8292: Fix three instances of truth tests on return values of filter() (which is always true in Python 3). ........ r83375 | antoine.pitrou | 2010-08-01 00:48:02 +0200 (So, 01 Aug 2010) | 3 lines Reorder entries by module lexicographic order ........ r83390 | georg.brandl | 2010-08-01 10:07:49 +0200 (So, 01 Aug 2010) | 1 line #8230: make Lib/test/sortperf.py run on Python 3. ........ r83394 | georg.brandl | 2010-08-01 10:46:24 +0200 (So, 01 Aug 2010) | 1 line No need to split this, there are enough long lines. ........ r83399 | georg.brandl | 2010-08-01 11:17:53 +0200 (So, 01 Aug 2010) | 1 line Package some new files that are needed for running the test suite from the MSI package. ........ r83404 | georg.brandl | 2010-08-01 16:25:22 +0200 (So, 01 Aug 2010) | 1 line #6439: fix argument type for PySys_SetArgvEx() and Py_SetProgramName() in Demo/embed code. ........ r83406 | georg.brandl | 2010-08-01 16:50:00 +0200 (So, 01 Aug 2010) | 1 line #8046: add context manager protocol support to mmap objects. Also add closed property. ........ Modified: python/branches/release27-maint/ (props changed) From python-checkins at python.org Sun Aug 1 21:23:36 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 1 Aug 2010 21:23:36 +0200 (CEST) Subject: [Python-checkins] r83435 - python/branches/release31-maint Message-ID: <20100801192336.8AB2AEEAF0@mail.python.org> Author: georg.brandl Date: Sun Aug 1 21:23:36 2010 New Revision: 83435 Log: Blocked revisions 83320-83323,83326-83327,83329,83339,83342-83343,83348-83351,83359-83361,83375,83394,83399,83406 via svnmerge ........ r83320 | georg.brandl | 2010-07-31 11:01:16 +0200 (Sa, 31 Jul 2010) | 1 line Update copyright years and add releases to release list. Also update Sphinx version number. ........ r83321 | georg.brandl | 2010-07-31 11:03:30 +0200 (Sa, 31 Jul 2010) | 1 line At least give IDLE 3.1 a release date. No further entries there for 3.2 though. ........ r83322 | tarek.ziade | 2010-07-31 11:10:51 +0200 (Sa, 31 Jul 2010) | 1 line reverted distutils doc to its 3.1 state ........ r83323 | georg.brandl | 2010-07-31 11:15:10 +0200 (Sa, 31 Jul 2010) | 1 line After distutils doc reversal, change back **bold todo** items to XXX comments. ........ r83326 | georg.brandl | 2010-07-31 12:08:09 +0200 (Sa, 31 Jul 2010) | 1 line Avoid triggering DeprecationWarnings in test_smtpd and smtpd. ........ r83327 | raymond.hettinger | 2010-07-31 12:11:39 +0200 (Sa, 31 Jul 2010) | 1 line Add functools.lfu_cache() and functools.lru_cache(). ........ r83329 | georg.brandl | 2010-07-31 12:16:21 +0200 (Sa, 31 Jul 2010) | 1 line Revert r83327. This will have to wait until after the alpha1 release. ........ r83339 | georg.brandl | 2010-07-31 13:00:47 +0200 (Sa, 31 Jul 2010) | 1 line Rewrap. ........ r83342 | georg.brandl | 2010-07-31 13:52:46 +0200 (Sa, 31 Jul 2010) | 1 line Import test_pdb with its full name, so that running python -m test.test_pdb succeeds. ........ r83343 | georg.brandl | 2010-07-31 14:06:51 +0200 (Sa, 31 Jul 2010) | 1 line From Martin: New UUIDs for the 3.2 release series. ........ r83348 | georg.brandl | 2010-07-31 20:05:35 +0200 (Sa, 31 Jul 2010) | 1 line Post-release updates. ........ r83349 | antoine.pitrou | 2010-07-31 20:08:33 +0200 (Sa, 31 Jul 2010) | 3 lines Add ssl changes to the 3.2 "what's new". ........ r83350 | georg.brandl | 2010-07-31 20:09:23 +0200 (Sa, 31 Jul 2010) | 1 line Re-commit r83327 now that the release is done. ........ r83351 | georg.brandl | 2010-07-31 20:09:46 +0200 (Sa, 31 Jul 2010) | 1 line Move news item to the correct position. ........ r83359 | georg.brandl | 2010-07-31 22:08:15 +0200 (Sa, 31 Jul 2010) | 1 line #1286: allow using fileinput.FileInput as context manager. ........ r83360 | georg.brandl | 2010-07-31 22:13:44 +0200 (Sa, 31 Jul 2010) | 1 line Clarify comment in comments test case explaining comment semantics. ........ r83361 | georg.brandl | 2010-07-31 23:04:00 +0200 (Sa, 31 Jul 2010) | 1 line #3788: more tests for http.cookies, now at 95% coverage. Also bring coding style in the module up to PEP 8, where it does not break backwards compatibility. ........ r83375 | antoine.pitrou | 2010-08-01 00:48:02 +0200 (So, 01 Aug 2010) | 3 lines Reorder entries by module lexicographic order ........ r83394 | georg.brandl | 2010-08-01 10:46:24 +0200 (So, 01 Aug 2010) | 1 line No need to split this, there are enough long lines. ........ r83399 | georg.brandl | 2010-08-01 11:17:53 +0200 (So, 01 Aug 2010) | 1 line Package some new files that are needed for running the test suite from the MSI package. ........ r83406 | georg.brandl | 2010-08-01 16:50:00 +0200 (So, 01 Aug 2010) | 1 line #8046: add context manager protocol support to mmap objects. Also add closed property. ........ Modified: python/branches/release31-maint/ (props changed) From python-checkins at python.org Sun Aug 1 21:33:15 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 1 Aug 2010 21:33:15 +0200 (CEST) Subject: [Python-checkins] r83436 - in python/branches/release27-maint: Doc/library/ftplib.rst Lib/bdb.py Lib/doctest.py Lib/pdb.py Lib/test/test_doctest.py Lib/test/test_pdb.py Misc/NEWS Misc/maintainers.rst Message-ID: <20100801193315.55832EEA47@mail.python.org> Author: georg.brandl Date: Sun Aug 1 21:33:15 2010 New Revision: 83436 Log: Merged revisions 83259,83261,83264-83265,83268-83269,83271-83272,83281 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83259 | georg.brandl | 2010-07-30 09:03:39 +0200 (Fr, 30 Jul 2010) | 1 line Clarification. ........ r83261 | georg.brandl | 2010-07-30 09:21:26 +0200 (Fr, 30 Jul 2010) | 1 line #9230: allow Pdb.checkline() to be called without a current frame, for setting breakpoints before starting debugging. ........ r83264 | georg.brandl | 2010-07-30 10:45:26 +0200 (Fr, 30 Jul 2010) | 1 line Document the "jump" command in pdb.__doc__, and add a version tag for "until X". ........ r83265 | georg.brandl | 2010-07-30 10:54:49 +0200 (Fr, 30 Jul 2010) | 1 line #8015: fix crash when entering an empty line for breakpoint commands. Also restore environment properly when an exception occurs during the definition of commands. ........ r83268 | georg.brandl | 2010-07-30 11:23:23 +0200 (Fr, 30 Jul 2010) | 2 lines Issue #8048: Prevent doctests from failing when sys.displayhook has been reassigned. ........ r83269 | georg.brandl | 2010-07-30 11:43:00 +0200 (Fr, 30 Jul 2010) | 1 line #6719: In pdb, do not stop somewhere in the encodings machinery if the source file to be debugged is in a non-builtin encoding. ........ r83271 | georg.brandl | 2010-07-30 11:59:28 +0200 (Fr, 30 Jul 2010) | 1 line #5727: Restore the ability to use readline when calling into pdb in doctests. ........ r83272 | georg.brandl | 2010-07-30 12:29:19 +0200 (Fr, 30 Jul 2010) | 1 line #5294: Fix the behavior of pdb "continue" command when called in the top-level debugged frame. ........ r83281 | georg.brandl | 2010-07-30 15:36:43 +0200 (Fr, 30 Jul 2010) | 1 line Add myself for pdb. ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Doc/library/ftplib.rst python/branches/release27-maint/Lib/bdb.py python/branches/release27-maint/Lib/doctest.py python/branches/release27-maint/Lib/pdb.py python/branches/release27-maint/Lib/test/test_doctest.py python/branches/release27-maint/Lib/test/test_pdb.py python/branches/release27-maint/Misc/NEWS python/branches/release27-maint/Misc/maintainers.rst Modified: python/branches/release27-maint/Doc/library/ftplib.rst ============================================================================== --- python/branches/release27-maint/Doc/library/ftplib.rst (original) +++ python/branches/release27-maint/Doc/library/ftplib.rst Sun Aug 1 21:33:15 2010 @@ -220,9 +220,9 @@ Retrieve a file or directory listing in ASCII transfer mode. *command* should be an appropriate ``RETR`` command (see :meth:`retrbinary`) or a command such as ``LIST``, ``NLST`` or ``MLSD`` (usually just the string - ``'LIST'``). The *callback* function is called for each line, with the - trailing CRLF stripped. The default *callback* prints the line to - ``sys.stdout``. + ``'LIST'``). The *callback* function is called for each line with a + string argument containing the line with the trailing CRLF stripped. + The default *callback* prints the line to ``sys.stdout``. .. method:: FTP.set_pasv(boolean) Modified: python/branches/release27-maint/Lib/bdb.py ============================================================================== --- python/branches/release27-maint/Lib/bdb.py (original) +++ python/branches/release27-maint/Lib/bdb.py Sun Aug 1 21:33:15 2010 @@ -109,6 +109,8 @@ self.is_skipped_module(frame.f_globals.get('__name__')): return False if frame is self.stopframe: + if self.stoplineno == -1: + return False return frame.f_lineno >= self.stoplineno while frame is not None and frame is not self.stopframe: if frame is self.botframe: @@ -166,10 +168,12 @@ but only if we are to stop at or just below this level.""" pass - def _set_stopinfo(self, stopframe, returnframe, stoplineno=-1): + def _set_stopinfo(self, stopframe, returnframe, stoplineno=0): self.stopframe = stopframe self.returnframe = returnframe self.quitting = 0 + # stoplineno >= 0 means: stop at line >= the stoplineno + # stoplineno -1 means: don't stop at all self.stoplineno = stoplineno # Derived classes and clients can call the following methods @@ -182,7 +186,7 @@ def set_step(self): """Stop after one line of code.""" - self._set_stopinfo(None,None) + self._set_stopinfo(None, None) def set_next(self, frame): """Stop on the next line in or below the given frame.""" @@ -209,7 +213,7 @@ def set_continue(self): # Don't stop except at breakpoints or when finished - self._set_stopinfo(self.botframe, None) + self._set_stopinfo(self.botframe, None, -1) if not self.breaks: # no breakpoints; run without debugger overhead sys.settrace(None) Modified: python/branches/release27-maint/Lib/doctest.py ============================================================================== --- python/branches/release27-maint/Lib/doctest.py (original) +++ python/branches/release27-maint/Lib/doctest.py Sun Aug 1 21:33:15 2010 @@ -335,6 +335,8 @@ self.__out = out self.__debugger_used = False pdb.Pdb.__init__(self, stdout=out) + # still use input() to get user input + self.use_rawinput = 1 def set_trace(self, frame=None): self.__debugger_used = True @@ -1381,12 +1383,17 @@ self.save_linecache_getlines = linecache.getlines linecache.getlines = self.__patched_linecache_getlines + # Make sure sys.displayhook just prints the value to stdout + save_displayhook = sys.displayhook + sys.displayhook = sys.__displayhook__ + try: return self.__run(test, compileflags, out) finally: sys.stdout = save_stdout pdb.set_trace = save_set_trace linecache.getlines = self.save_linecache_getlines + sys.displayhook = save_displayhook if clear_globs: test.globs.clear() Modified: python/branches/release27-maint/Lib/pdb.py ============================================================================== --- python/branches/release27-maint/Lib/pdb.py (original) +++ python/branches/release27-maint/Lib/pdb.py Sun Aug 1 21:33:15 2010 @@ -183,14 +183,18 @@ def user_return(self, frame, return_value): """This function is called when a return trap is set here.""" + if self._wait_for_mainpyfile: + return frame.f_locals['__return__'] = return_value print >>self.stdout, '--Return--' self.interaction(frame, None) def user_exception(self, frame, exc_info): - exc_type, exc_value, exc_traceback = exc_info """This function is called if an exception occurs, but only if we are to stop at or just below this level.""" + if self._wait_for_mainpyfile: + return + exc_type, exc_value, exc_traceback = exc_info frame.f_locals['__exception__'] = exc_type, exc_value if type(exc_type) == type(''): exc_type_name = exc_type @@ -277,8 +281,10 @@ return self.handle_command_def(line) def handle_command_def(self,line): - """ Handles one command line during command list definition. """ + """Handles one command line during command list definition.""" cmd, arg, line = self.parseline(line) + if not cmd: + return if cmd == 'silent': self.commands_silent[self.commands_bnum] = True return # continue to handle other cmd def in the cmd list @@ -286,7 +292,7 @@ self.cmdqueue = [] return 1 # end of cmd list cmdlist = self.commands[self.commands_bnum] - if (arg): + if arg: cmdlist.append(cmd+' '+arg) else: cmdlist.append(cmd) @@ -329,9 +335,11 @@ prompt_back = self.prompt self.prompt = '(com) ' self.commands_defining = True - self.cmdloop() - self.commands_defining = False - self.prompt = prompt_back + try: + self.cmdloop() + finally: + self.commands_defining = False + self.prompt = prompt_back def do_break(self, arg, temporary = 0): # break [ ([filename:]lineno | function) [, "condition"] ] @@ -467,7 +475,10 @@ Return `lineno` if it is, 0 if not (e.g. a docstring, comment, blank line or EOF). Warning: testing is not comprehensive. """ - line = linecache.getline(filename, lineno, self.curframe.f_globals) + # this method should be callable before starting debugging, so default + # to "no globals" if there is no current frame + globs = self.curframe.f_globals if hasattr(self, 'curframe') else None + line = linecache.getline(filename, lineno, globs) if not line: print >>self.stdout, 'End of file' return 0 @@ -1298,7 +1309,7 @@ # changed by the user from the command line. There is a "restart" command # which allows explicit specification of command line arguments. pdb = Pdb() - while 1: + while True: try: pdb._runscript(mainpyfile) if pdb._user_requested_quit: Modified: python/branches/release27-maint/Lib/test/test_doctest.py ============================================================================== --- python/branches/release27-maint/Lib/test/test_doctest.py (original) +++ python/branches/release27-maint/Lib/test/test_doctest.py Sun Aug 1 21:33:15 2010 @@ -981,6 +981,35 @@ ZeroDivisionError: integer division or modulo by zero TestResults(failed=1, attempted=1) """ + def displayhook(): r""" +Test that changing sys.displayhook doesn't matter for doctest. + + >>> import sys + >>> orig_displayhook = sys.displayhook + >>> def my_displayhook(x): + ... print('hi!') + >>> sys.displayhook = my_displayhook + >>> def f(): + ... ''' + ... >>> 3 + ... 3 + ... ''' + >>> test = doctest.DocTestFinder().find(f)[0] + >>> r = doctest.DocTestRunner(verbose=False).run(test) + >>> post_displayhook = sys.displayhook + + We need to restore sys.displayhook now, so that we'll be able to test + results. + + >>> sys.displayhook = orig_displayhook + + Ok, now we can check that everything is ok. + + >>> r + TestResults(failed=0, attempted=1) + >>> post_displayhook is my_displayhook + True +""" def optionflags(): r""" Tests of `DocTestRunner`'s option flag handling. Modified: python/branches/release27-maint/Lib/test/test_pdb.py ============================================================================== --- python/branches/release27-maint/Lib/test/test_pdb.py (original) +++ python/branches/release27-maint/Lib/test/test_pdb.py Sun Aug 1 21:33:15 2010 @@ -126,6 +126,48 @@ """ +def test_pdb_continue_in_bottomframe(): + """Test that "continue" and "next" work properly in bottom frame (issue #5294). + + >>> def test_function(): + ... import pdb, sys; inst = pdb.Pdb() + ... inst.set_trace() + ... inst.botframe = sys._getframe() # hackery to get the right botframe + ... print(1) + ... print(2) + ... print(3) + ... print(4) + + >>> with PdbTestInput([ + ... 'next', + ... 'break 7', + ... 'continue', + ... 'next', + ... 'continue', + ... 'continue', + ... ]): + ... test_function() + > (4)test_function() + -> inst.botframe = sys._getframe() # hackery to get the right botframe + (Pdb) next + > (5)test_function() + -> print(1) + (Pdb) break 7 + Breakpoint 1 at :7 + (Pdb) continue + 1 + 2 + > (7)test_function() + -> print(3) + (Pdb) next + 3 + > (8)test_function() + -> print(4) + (Pdb) continue + 4 + """ + + def test_main(): from test import test_pdb test_support.run_doctest(test_pdb, verbosity=True) Modified: python/branches/release27-maint/Misc/NEWS ============================================================================== --- python/branches/release27-maint/Misc/NEWS (original) +++ python/branches/release27-maint/Misc/NEWS Sun Aug 1 21:33:15 2010 @@ -24,6 +24,21 @@ Library ------- +- Issue #5294: Fix the behavior of pdb's "continue" command when called + in the top-level debugged frame. + +- Issue #5727: Restore the ability to use readline when calling into pdb + in doctests. + +- Issue #6719: In pdb, do not stop somewhere in the encodings machinery + if the source file to be debugged is in a non-builtin encoding. + +- Issue #8048: Prevent doctests from failing when sys.displayhook has + been reassigned. + +- Issue #8015: In pdb, do not crash when an empty line is entered as + a breakpoint command. + - Issue #9448: Fix a leak of OS resources (mutexes or semaphores) when re-initializing a buffered IO object by calling its ``__init__`` method. Modified: python/branches/release27-maint/Misc/maintainers.rst ============================================================================== --- python/branches/release27-maint/Misc/maintainers.rst (original) +++ python/branches/release27-maint/Misc/maintainers.rst Sun Aug 1 21:33:15 2010 @@ -151,7 +151,7 @@ os loewis ossaudiodev parser -pdb +pdb georg.brandl pickle alexandre.vassalotti, pitrou pickletools alexandre.vassalotti pipes From python-checkins at python.org Sun Aug 1 21:35:17 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 1 Aug 2010 21:35:17 +0200 (CEST) Subject: [Python-checkins] r83437 - in python/branches/release31-maint: Doc/library/ftplib.rst Lib/bdb.py Lib/doctest.py Lib/pdb.py Lib/test/test_doctest.py Lib/test/test_pdb.py Misc/NEWS Misc/maintainers.rst Message-ID: <20100801193517.266FBEE992@mail.python.org> Author: georg.brandl Date: Sun Aug 1 21:35:16 2010 New Revision: 83437 Log: Merged revisions 83259,83261,83264-83265,83268-83269,83271-83272,83281 via svnmerge from svn+ssh://svn.python.org/python/branches/py3k ........ r83259 | georg.brandl | 2010-07-30 09:03:39 +0200 (Fr, 30 Jul 2010) | 1 line Clarification. ........ r83261 | georg.brandl | 2010-07-30 09:21:26 +0200 (Fr, 30 Jul 2010) | 1 line #9230: allow Pdb.checkline() to be called without a current frame, for setting breakpoints before starting debugging. ........ r83264 | georg.brandl | 2010-07-30 10:45:26 +0200 (Fr, 30 Jul 2010) | 1 line Document the "jump" command in pdb.__doc__, and add a version tag for "until X". ........ r83265 | georg.brandl | 2010-07-30 10:54:49 +0200 (Fr, 30 Jul 2010) | 1 line #8015: fix crash when entering an empty line for breakpoint commands. Also restore environment properly when an exception occurs during the definition of commands. ........ r83268 | georg.brandl | 2010-07-30 11:23:23 +0200 (Fr, 30 Jul 2010) | 2 lines Issue #8048: Prevent doctests from failing when sys.displayhook has been reassigned. ........ r83269 | georg.brandl | 2010-07-30 11:43:00 +0200 (Fr, 30 Jul 2010) | 1 line #6719: In pdb, do not stop somewhere in the encodings machinery if the source file to be debugged is in a non-builtin encoding. ........ r83271 | georg.brandl | 2010-07-30 11:59:28 +0200 (Fr, 30 Jul 2010) | 1 line #5727: Restore the ability to use readline when calling into pdb in doctests. ........ r83272 | georg.brandl | 2010-07-30 12:29:19 +0200 (Fr, 30 Jul 2010) | 1 line #5294: Fix the behavior of pdb "continue" command when called in the top-level debugged frame. ........ r83281 | georg.brandl | 2010-07-30 15:36:43 +0200 (Fr, 30 Jul 2010) | 1 line Add myself for pdb. ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Doc/library/ftplib.rst python/branches/release31-maint/Lib/bdb.py python/branches/release31-maint/Lib/doctest.py python/branches/release31-maint/Lib/pdb.py python/branches/release31-maint/Lib/test/test_doctest.py python/branches/release31-maint/Lib/test/test_pdb.py python/branches/release31-maint/Misc/NEWS python/branches/release31-maint/Misc/maintainers.rst Modified: python/branches/release31-maint/Doc/library/ftplib.rst ============================================================================== --- python/branches/release31-maint/Doc/library/ftplib.rst (original) +++ python/branches/release31-maint/Doc/library/ftplib.rst Sun Aug 1 21:35:16 2010 @@ -180,9 +180,9 @@ Retrieve a file or directory listing in ASCII transfer mode. *cmd* should be an appropriate ``RETR`` command (see :meth:`retrbinary`) or a command such as ``LIST``, ``NLST`` or ``MLSD`` (usually just the string - ``'LIST'``). The *callback* function is called for each line, with the - trailing CRLF stripped. The default *callback* prints the line to - ``sys.stdout``. + ``'LIST'``). The *callback* function is called for each line with a + string argument containing the line with the trailing CRLF stripped. + The default *callback* prints the line to ``sys.stdout``. .. method:: FTP.set_pasv(boolean) Modified: python/branches/release31-maint/Lib/bdb.py ============================================================================== --- python/branches/release31-maint/Lib/bdb.py (original) +++ python/branches/release31-maint/Lib/bdb.py Sun Aug 1 21:35:16 2010 @@ -109,6 +109,8 @@ self.is_skipped_module(frame.f_globals.get('__name__')): return False if frame is self.stopframe: + if self.stoplineno == -1: + return False return frame.f_lineno >= self.stoplineno while frame is not None and frame is not self.stopframe: if frame is self.botframe: @@ -165,10 +167,12 @@ but only if we are to stop at or just below this level.""" pass - def _set_stopinfo(self, stopframe, returnframe, stoplineno=-1): + def _set_stopinfo(self, stopframe, returnframe, stoplineno=0): self.stopframe = stopframe self.returnframe = returnframe self.quitting = 0 + # stoplineno >= 0 means: stop at line >= the stoplineno + # stoplineno -1 means: don't stop at all self.stoplineno = stoplineno # Derived classes and clients can call the following methods @@ -181,7 +185,7 @@ def set_step(self): """Stop after one line of code.""" - self._set_stopinfo(None,None) + self._set_stopinfo(None, None) def set_next(self, frame): """Stop on the next line in or below the given frame.""" @@ -208,7 +212,7 @@ def set_continue(self): # Don't stop except at breakpoints or when finished - self._set_stopinfo(self.botframe, None) + self._set_stopinfo(self.botframe, None, -1) if not self.breaks: # no breakpoints; run without debugger overhead sys.settrace(None) Modified: python/branches/release31-maint/Lib/doctest.py ============================================================================== --- python/branches/release31-maint/Lib/doctest.py (original) +++ python/branches/release31-maint/Lib/doctest.py Sun Aug 1 21:35:16 2010 @@ -318,6 +318,8 @@ self.__out = out self.__debugger_used = False pdb.Pdb.__init__(self, stdout=out) + # still use input() to get user input + self.use_rawinput = 1 def set_trace(self, frame=None): self.__debugger_used = True @@ -1379,12 +1381,17 @@ self.save_linecache_getlines = linecache.getlines linecache.getlines = self.__patched_linecache_getlines + # Make sure sys.displayhook just prints the value to stdout + save_displayhook = sys.displayhook + sys.displayhook = sys.__displayhook__ + try: return self.__run(test, compileflags, out) finally: sys.stdout = save_stdout pdb.set_trace = save_set_trace linecache.getlines = self.save_linecache_getlines + sys.displayhook = save_displayhook if clear_globs: test.globs.clear() import builtins Modified: python/branches/release31-maint/Lib/pdb.py ============================================================================== --- python/branches/release31-maint/Lib/pdb.py (original) +++ python/branches/release31-maint/Lib/pdb.py Sun Aug 1 21:35:16 2010 @@ -183,6 +183,8 @@ def user_return(self, frame, return_value): """This function is called when a return trap is set here.""" + if self._wait_for_mainpyfile: + return frame.f_locals['__return__'] = return_value print('--Return--', file=self.stdout) self.interaction(frame, None) @@ -190,6 +192,8 @@ def user_exception(self, frame, exc_info): """This function is called if an exception occurs, but only if we are to stop at or just below this level.""" + if self._wait_for_mainpyfile: + return exc_type, exc_value, exc_traceback = exc_info frame.f_locals['__exception__'] = exc_type, exc_value exc_type_name = exc_type.__name__ @@ -275,8 +279,10 @@ return self.handle_command_def(line) def handle_command_def(self,line): - """ Handles one command line during command list definition. """ + """Handles one command line during command list definition.""" cmd, arg, line = self.parseline(line) + if not cmd: + return if cmd == 'silent': self.commands_silent[self.commands_bnum] = True return # continue to handle other cmd def in the cmd list @@ -284,7 +290,7 @@ self.cmdqueue = [] return 1 # end of cmd list cmdlist = self.commands[self.commands_bnum] - if (arg): + if arg: cmdlist.append(cmd+' '+arg) else: cmdlist.append(cmd) @@ -327,9 +333,11 @@ prompt_back = self.prompt self.prompt = '(com) ' self.commands_defining = True - self.cmdloop() - self.commands_defining = False - self.prompt = prompt_back + try: + self.cmdloop() + finally: + self.commands_defining = False + self.prompt = prompt_back def do_break(self, arg, temporary = 0): # break [ ([filename:]lineno | function) [, "condition"] ] @@ -465,7 +473,10 @@ Return `lineno` if it is, 0 if not (e.g. a docstring, comment, blank line or EOF). Warning: testing is not comprehensive. """ - line = linecache.getline(filename, lineno, self.curframe.f_globals) + # this method should be callable before starting debugging, so default + # to "no globals" if there is no current frame + globs = self.curframe.f_globals if hasattr(self, 'curframe') else None + line = linecache.getline(filename, lineno, globs) if not line: print('End of file', file=self.stdout) return 0 @@ -1293,7 +1304,7 @@ # changed by the user from the command line. There is a "restart" command # which allows explicit specification of command line arguments. pdb = Pdb() - while 1: + while True: try: pdb._runscript(mainpyfile) if pdb._user_requested_quit: Modified: python/branches/release31-maint/Lib/test/test_doctest.py ============================================================================== --- python/branches/release31-maint/Lib/test/test_doctest.py (original) +++ python/branches/release31-maint/Lib/test/test_doctest.py Sun Aug 1 21:35:16 2010 @@ -907,6 +907,35 @@ ZeroDivisionError: integer division or modulo by zero TestResults(failed=1, attempted=1) """ + def displayhook(): r""" +Test that changing sys.displayhook doesn't matter for doctest. + + >>> import sys + >>> orig_displayhook = sys.displayhook + >>> def my_displayhook(x): + ... print('hi!') + >>> sys.displayhook = my_displayhook + >>> def f(): + ... ''' + ... >>> 3 + ... 3 + ... ''' + >>> test = doctest.DocTestFinder().find(f)[0] + >>> r = doctest.DocTestRunner(verbose=False).run(test) + >>> post_displayhook = sys.displayhook + + We need to restore sys.displayhook now, so that we'll be able to test + results. + + >>> sys.displayhook = orig_displayhook + + Ok, now we can check that everything is ok. + + >>> r + TestResults(failed=0, attempted=1) + >>> post_displayhook is my_displayhook + True +""" def optionflags(): r""" Tests of `DocTestRunner`'s option flag handling. Modified: python/branches/release31-maint/Lib/test/test_pdb.py ============================================================================== --- python/branches/release31-maint/Lib/test/test_pdb.py (original) +++ python/branches/release31-maint/Lib/test/test_pdb.py Sun Aug 1 21:35:16 2010 @@ -126,6 +126,48 @@ """ +def test_pdb_continue_in_bottomframe(): + """Test that "continue" and "next" work properly in bottom frame (issue #5294). + + >>> def test_function(): + ... import pdb, sys; inst = pdb.Pdb() + ... inst.set_trace() + ... inst.botframe = sys._getframe() # hackery to get the right botframe + ... print(1) + ... print(2) + ... print(3) + ... print(4) + + >>> with PdbTestInput([ + ... 'next', + ... 'break 7', + ... 'continue', + ... 'next', + ... 'continue', + ... 'continue', + ... ]): + ... test_function() + > (4)test_function() + -> inst.botframe = sys._getframe() # hackery to get the right botframe + (Pdb) next + > (5)test_function() + -> print(1) + (Pdb) break 7 + Breakpoint 1 at :7 + (Pdb) continue + 1 + 2 + > (7)test_function() + -> print(3) + (Pdb) next + 3 + > (8)test_function() + -> print(4) + (Pdb) continue + 4 + """ + + def test_main(): from test import test_pdb support.run_doctest(test_pdb, verbosity=True) Modified: python/branches/release31-maint/Misc/NEWS ============================================================================== --- python/branches/release31-maint/Misc/NEWS (original) +++ python/branches/release31-maint/Misc/NEWS Sun Aug 1 21:35:16 2010 @@ -87,6 +87,21 @@ - Issue #7909: Do not touch paths with the special prefixes ``\\.\`` or ``\\?\`` in ntpath.normpath(). +- Issue #5294: Fix the behavior of pdb's "continue" command when called + in the top-level debugged frame. + +- Issue #5727: Restore the ability to use readline when calling into pdb + in doctests. + +- Issue #6719: In pdb, do not stop somewhere in the encodings machinery + if the source file to be debugged is in a non-builtin encoding. + +- Issue #8048: Prevent doctests from failing when sys.displayhook has + been reassigned. + +- Issue #8015: In pdb, do not crash when an empty line is entered as + a breakpoint command. + - Issue #5146: Handle UID THREAD command correctly in imaplib. - Issue #5147: Fix the header generated for cookie files written by Modified: python/branches/release31-maint/Misc/maintainers.rst ============================================================================== --- python/branches/release31-maint/Misc/maintainers.rst (original) +++ python/branches/release31-maint/Misc/maintainers.rst Sun Aug 1 21:35:16 2010 @@ -147,7 +147,7 @@ os loewis ossaudiodev parser -pdb +pdb georg.brandl pickle alexandre.vassalotti, pitrou pickletools alexandre.vassalotti pipes From python-checkins at python.org Sun Aug 1 21:36:38 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 1 Aug 2010 21:36:38 +0200 (CEST) Subject: [Python-checkins] r83438 - python/branches/release31-maint Message-ID: <20100801193638.389B6EE992@mail.python.org> Author: georg.brandl Date: Sun Aug 1 21:36:38 2010 New Revision: 83438 Log: Blocked revisions 83260,83262-83263,83266,83270,83274-83275,83283-83287,83291-83293,83308,83315-83318 via svnmerge ........ r83260 | georg.brandl | 2010-07-30 09:14:01 +0200 (Fr, 30 Jul 2010) | 1 line #4179: In pdb, allow "list ." as a command to return to the currently debugged line. ........ r83262 | georg.brandl | 2010-07-30 10:29:39 +0200 (Fr, 30 Jul 2010) | 1 line #1437051: allow "continue"/"next"/etc. in .pdbrc, also add pdb -c option to give these commands. This allows to run a script until an exception occurs. ........ r83263 | georg.brandl | 2010-07-30 10:43:32 +0200 (Fr, 30 Jul 2010) | 1 line Allow giving an explicit line number to "until". ........ r83266 | georg.brandl | 2010-07-30 11:14:20 +0200 (Fr, 30 Jul 2010) | 1 line #1472251: remove addition of "\n" to code given to pdb.run[eval](), the bug in exec() that made this necessary has been fixed. Also document that you can give code objects to run() and runeval(), and add some tests to test_pdb. ........ r83270 | georg.brandl | 2010-07-30 11:54:44 +0200 (Fr, 30 Jul 2010) | 1 line Remove redundant import. ........ r83274 | georg.brandl | 2010-07-30 13:31:03 +0200 (Fr, 30 Jul 2010) | 1 line #3143: enable "collapsible sidebar" feature of new Sphinx version. ........ r83275 | georg.brandl | 2010-07-30 14:01:20 +0200 (Fr, 30 Jul 2010) | 1 line #809887: improve pdb feedback for breakpoint-related actions. Also add a functional test for these commands. ........ r83283 | georg.brandl | 2010-07-30 16:16:43 +0200 (Fr, 30 Jul 2010) | 1 line #7964 followup: add test case to ensure issue remains fixed. ........ r83284 | georg.brandl | 2010-07-30 17:01:23 +0200 (Fr, 30 Jul 2010) | 1 line Add Breakpoint.bpformat(), which returns the info usually printed by bpprint(). Necessary for major refactoring of pdb output handling. ........ r83285 | georg.brandl | 2010-07-30 17:33:52 +0200 (Fr, 30 Jul 2010) | 1 line pdb now has its own tests. ........ r83286 | georg.brandl | 2010-07-30 18:00:46 +0200 (Fr, 30 Jul 2010) | 7 lines Several enhancements to pdb and its test suite. * added basic test for basic commands * removed duplication of command docs, and moved them to their implementation * unified and useful display of exceptions * output messages and errors using overridable methods (also fixes #1503502) ........ r83287 | georg.brandl | 2010-07-30 19:04:28 +0200 (Fr, 30 Jul 2010) | 1 line Add "longlist" and "source" commands, ideas borrowed from pdb++ by Antonio Cuni. ........ r83291 | georg.brandl | 2010-07-30 20:08:12 +0200 (Fr, 30 Jul 2010) | 1 line Fix source finding if the given frame is a module-level frame. ........ r83292 | georg.brandl | 2010-07-30 20:15:16 +0200 (Fr, 30 Jul 2010) | 1 line Test that "source" with nonexisting things works as expected. ........ r83293 | georg.brandl | 2010-07-30 20:46:38 +0200 (Fr, 30 Jul 2010) | 1 line Show the traceback line numbers as well as the current line numbers if an exception is being debugged. Courtesy of pdb++ by Antonio Cuni. Also document -> and >> markers for "list". ........ r83308 | georg.brandl | 2010-07-31 00:20:16 +0200 (Sa, 31 Jul 2010) | 1 line Part of #7245: when KeyboardInterrupt is raised while defining commands, restore the old commands instead of producing a traceback. ........ r83315 | georg.brandl | 2010-07-31 10:14:16 +0200 (Sa, 31 Jul 2010) | 1 line Fix pdb test failures on the buildbots. ........ r83316 | georg.brandl | 2010-07-31 10:20:02 +0200 (Sa, 31 Jul 2010) | 1 line Make urllib tests pass for now. Will figure out what the correct semantics should be after release. ........ r83317 | georg.brandl | 2010-07-31 10:27:46 +0200 (Sa, 31 Jul 2010) | 1 line Update pydoc topics and adapt Topics builder to Sphinx 1.0. ........ r83318 | georg.brandl | 2010-07-31 10:56:11 +0200 (Sa, 31 Jul 2010) | 1 line Bump versions and review NEWS file. ........ Modified: python/branches/release31-maint/ (props changed) From python-checkins at python.org Sun Aug 1 21:36:43 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 1 Aug 2010 21:36:43 +0200 (CEST) Subject: [Python-checkins] r83439 - python/branches/release27-maint Message-ID: <20100801193643.DC6E0EEAB1@mail.python.org> Author: georg.brandl Date: Sun Aug 1 21:36:43 2010 New Revision: 83439 Log: Blocked revisions 83260,83262-83263,83266,83270,83274-83275,83283-83287,83291-83293,83308,83315-83318 via svnmerge ........ r83260 | georg.brandl | 2010-07-30 09:14:01 +0200 (Fr, 30 Jul 2010) | 1 line #4179: In pdb, allow "list ." as a command to return to the currently debugged line. ........ r83262 | georg.brandl | 2010-07-30 10:29:39 +0200 (Fr, 30 Jul 2010) | 1 line #1437051: allow "continue"/"next"/etc. in .pdbrc, also add pdb -c option to give these commands. This allows to run a script until an exception occurs. ........ r83263 | georg.brandl | 2010-07-30 10:43:32 +0200 (Fr, 30 Jul 2010) | 1 line Allow giving an explicit line number to "until". ........ r83266 | georg.brandl | 2010-07-30 11:14:20 +0200 (Fr, 30 Jul 2010) | 1 line #1472251: remove addition of "\n" to code given to pdb.run[eval](), the bug in exec() that made this necessary has been fixed. Also document that you can give code objects to run() and runeval(), and add some tests to test_pdb. ........ r83270 | georg.brandl | 2010-07-30 11:54:44 +0200 (Fr, 30 Jul 2010) | 1 line Remove redundant import. ........ r83274 | georg.brandl | 2010-07-30 13:31:03 +0200 (Fr, 30 Jul 2010) | 1 line #3143: enable "collapsible sidebar" feature of new Sphinx version. ........ r83275 | georg.brandl | 2010-07-30 14:01:20 +0200 (Fr, 30 Jul 2010) | 1 line #809887: improve pdb feedback for breakpoint-related actions. Also add a functional test for these commands. ........ r83283 | georg.brandl | 2010-07-30 16:16:43 +0200 (Fr, 30 Jul 2010) | 1 line #7964 followup: add test case to ensure issue remains fixed. ........ r83284 | georg.brandl | 2010-07-30 17:01:23 +0200 (Fr, 30 Jul 2010) | 1 line Add Breakpoint.bpformat(), which returns the info usually printed by bpprint(). Necessary for major refactoring of pdb output handling. ........ r83285 | georg.brandl | 2010-07-30 17:33:52 +0200 (Fr, 30 Jul 2010) | 1 line pdb now has its own tests. ........ r83286 | georg.brandl | 2010-07-30 18:00:46 +0200 (Fr, 30 Jul 2010) | 7 lines Several enhancements to pdb and its test suite. * added basic test for basic commands * removed duplication of command docs, and moved them to their implementation * unified and useful display of exceptions * output messages and errors using overridable methods (also fixes #1503502) ........ r83287 | georg.brandl | 2010-07-30 19:04:28 +0200 (Fr, 30 Jul 2010) | 1 line Add "longlist" and "source" commands, ideas borrowed from pdb++ by Antonio Cuni. ........ r83291 | georg.brandl | 2010-07-30 20:08:12 +0200 (Fr, 30 Jul 2010) | 1 line Fix source finding if the given frame is a module-level frame. ........ r83292 | georg.brandl | 2010-07-30 20:15:16 +0200 (Fr, 30 Jul 2010) | 1 line Test that "source" with nonexisting things works as expected. ........ r83293 | georg.brandl | 2010-07-30 20:46:38 +0200 (Fr, 30 Jul 2010) | 1 line Show the traceback line numbers as well as the current line numbers if an exception is being debugged. Courtesy of pdb++ by Antonio Cuni. Also document -> and >> markers for "list". ........ r83308 | georg.brandl | 2010-07-31 00:20:16 +0200 (Sa, 31 Jul 2010) | 1 line Part of #7245: when KeyboardInterrupt is raised while defining commands, restore the old commands instead of producing a traceback. ........ r83315 | georg.brandl | 2010-07-31 10:14:16 +0200 (Sa, 31 Jul 2010) | 1 line Fix pdb test failures on the buildbots. ........ r83316 | georg.brandl | 2010-07-31 10:20:02 +0200 (Sa, 31 Jul 2010) | 1 line Make urllib tests pass for now. Will figure out what the correct semantics should be after release. ........ r83317 | georg.brandl | 2010-07-31 10:27:46 +0200 (Sa, 31 Jul 2010) | 1 line Update pydoc topics and adapt Topics builder to Sphinx 1.0. ........ r83318 | georg.brandl | 2010-07-31 10:56:11 +0200 (Sa, 31 Jul 2010) | 1 line Bump versions and review NEWS file. ........ Modified: python/branches/release27-maint/ (props changed) From python-checkins at python.org Sun Aug 1 22:08:46 2010 From: python-checkins at python.org (antoine.pitrou) Date: Sun, 1 Aug 2010 22:08:46 +0200 (CEST) Subject: [Python-checkins] r83440 - in python/branches/py3k: Lib/test/test_bz2.py Misc/NEWS Modules/bz2module.c Message-ID: <20100801200846.6CFE2EE99A@mail.python.org> Author: antoine.pitrou Date: Sun Aug 1 22:08:46 2010 New Revision: 83440 Log: Issue #8397: Raise an error when attempting to mix iteration and regular reads on a BZ2File object, rather than returning incorrect results. Modified: python/branches/py3k/Lib/test/test_bz2.py python/branches/py3k/Misc/NEWS python/branches/py3k/Modules/bz2module.c Modified: python/branches/py3k/Lib/test/test_bz2.py ============================================================================== --- python/branches/py3k/Lib/test/test_bz2.py (original) +++ python/branches/py3k/Lib/test/test_bz2.py Sun Aug 1 22:08:46 2010 @@ -304,6 +304,24 @@ finally: f.close() + def testMixedIterationReads(self): + # Issue #8397: mixed iteration and reads should be forbidden. + f = bz2.BZ2File(self.filename, 'wb') + try: + # The internal buffer size is hard-wired to 8192 bytes, we must + # write out more than that for the test to stop half through + # the buffer. + f.write(self.TEXT * 100) + finally: + f.close() + f = bz2.BZ2File(self.filename, 'rb') + try: + next(f) + self.assertRaises(ValueError, f.read) + self.assertRaises(ValueError, f.readline) + self.assertRaises(ValueError, f.readlines) + finally: + f.close() class BZ2CompressorTest(BaseTest): def testCompress(self): Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Sun Aug 1 22:08:46 2010 @@ -29,6 +29,9 @@ Library ------- +- Issue #8397: Raise an error when attempting to mix iteration and regular + reads on a BZ2File object, rather than returning incorrect results. + - Issue #9448: Fix a leak of OS resources (mutexes or semaphores) when re-initializing a buffered IO object by calling its ``__init__`` method. Modified: python/branches/py3k/Modules/bz2module.c ============================================================================== --- python/branches/py3k/Modules/bz2module.c (original) +++ python/branches/py3k/Modules/bz2module.c Sun Aug 1 22:08:46 2010 @@ -138,6 +138,22 @@ /* ===================================================================== */ /* Utility functions. */ +/* Refuse regular I/O if there's data in the iteration-buffer. + * Mixing them would cause data to arrive out of order, as the read* + * methods don't use the iteration buffer. */ +static int +check_iterbuffered(BZ2FileObject *f) +{ + if (f->f_buf != NULL && + (f->f_bufend - f->f_bufptr) > 0 && + f->f_buf[0] != '\0') { + PyErr_SetString(PyExc_ValueError, + "Mixing iteration and read methods would lose data"); + return -1; + } + return 0; +} + static int Util_CatchBZ2Error(int bzerror) { @@ -427,6 +443,10 @@ goto cleanup; } + /* refuse to mix with f.next() */ + if (check_iterbuffered(self)) + goto cleanup; + if (bytesrequested < 0) buffersize = Util_NewBufferSize((size_t)0); else @@ -516,6 +536,10 @@ goto cleanup; } + /* refuse to mix with f.next() */ + if (check_iterbuffered(self)) + goto cleanup; + if (sizehint == 0) ret = PyBytes_FromStringAndSize("", 0); else @@ -573,6 +597,10 @@ goto cleanup; } + /* refuse to mix with f.next() */ + if (check_iterbuffered(self)) + goto cleanup; + if ((list = PyList_New(0)) == NULL) goto cleanup; From python-checkins at python.org Sun Aug 1 22:12:04 2010 From: python-checkins at python.org (antoine.pitrou) Date: Sun, 1 Aug 2010 22:12:04 +0200 (CEST) Subject: [Python-checkins] r83441 - in python/branches/release31-maint: Lib/test/test_bz2.py Misc/NEWS Modules/bz2module.c Message-ID: <20100801201204.C7913EE9D2@mail.python.org> Author: antoine.pitrou Date: Sun Aug 1 22:12:04 2010 New Revision: 83441 Log: Merged revisions 83440 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83440 | antoine.pitrou | 2010-08-01 22:08:46 +0200 (dim., 01 ao?t 2010) | 4 lines Issue #8397: Raise an error when attempting to mix iteration and regular reads on a BZ2File object, rather than returning incorrect results. ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Lib/test/test_bz2.py python/branches/release31-maint/Misc/NEWS python/branches/release31-maint/Modules/bz2module.c Modified: python/branches/release31-maint/Lib/test/test_bz2.py ============================================================================== --- python/branches/release31-maint/Lib/test/test_bz2.py (original) +++ python/branches/release31-maint/Lib/test/test_bz2.py Sun Aug 1 22:12:04 2010 @@ -300,6 +300,24 @@ finally: f.close() + def testMixedIterationReads(self): + # Issue #8397: mixed iteration and reads should be forbidden. + f = bz2.BZ2File(self.filename, 'wb') + try: + # The internal buffer size is hard-wired to 8192 bytes, we must + # write out more than that for the test to stop half through + # the buffer. + f.write(self.TEXT * 100) + finally: + f.close() + f = bz2.BZ2File(self.filename, 'rb') + try: + next(f) + self.assertRaises(ValueError, f.read) + self.assertRaises(ValueError, f.readline) + self.assertRaises(ValueError, f.readlines) + finally: + f.close() class BZ2CompressorTest(BaseTest): def testCompress(self): Modified: python/branches/release31-maint/Misc/NEWS ============================================================================== --- python/branches/release31-maint/Misc/NEWS (original) +++ python/branches/release31-maint/Misc/NEWS Sun Aug 1 22:12:04 2010 @@ -84,6 +84,9 @@ Library ------- +- Issue #8397: Raise an error when attempting to mix iteration and regular + reads on a BZ2File object, rather than returning incorrect results. + - Issue #7909: Do not touch paths with the special prefixes ``\\.\`` or ``\\?\`` in ntpath.normpath(). Modified: python/branches/release31-maint/Modules/bz2module.c ============================================================================== --- python/branches/release31-maint/Modules/bz2module.c (original) +++ python/branches/release31-maint/Modules/bz2module.c Sun Aug 1 22:12:04 2010 @@ -138,6 +138,22 @@ /* ===================================================================== */ /* Utility functions. */ +/* Refuse regular I/O if there's data in the iteration-buffer. + * Mixing them would cause data to arrive out of order, as the read* + * methods don't use the iteration buffer. */ +static int +check_iterbuffered(BZ2FileObject *f) +{ + if (f->f_buf != NULL && + (f->f_bufend - f->f_bufptr) > 0 && + f->f_buf[0] != '\0') { + PyErr_SetString(PyExc_ValueError, + "Mixing iteration and read methods would lose data"); + return -1; + } + return 0; +} + static int Util_CatchBZ2Error(int bzerror) { @@ -427,6 +443,10 @@ goto cleanup; } + /* refuse to mix with f.next() */ + if (check_iterbuffered(self)) + goto cleanup; + if (bytesrequested < 0) buffersize = Util_NewBufferSize((size_t)0); else @@ -516,6 +536,10 @@ goto cleanup; } + /* refuse to mix with f.next() */ + if (check_iterbuffered(self)) + goto cleanup; + if (sizehint == 0) ret = PyBytes_FromStringAndSize("", 0); else @@ -573,6 +597,10 @@ goto cleanup; } + /* refuse to mix with f.next() */ + if (check_iterbuffered(self)) + goto cleanup; + if ((list = PyList_New(0)) == NULL) goto cleanup; From python-checkins at python.org Sun Aug 1 22:13:11 2010 From: python-checkins at python.org (antoine.pitrou) Date: Sun, 1 Aug 2010 22:13:11 +0200 (CEST) Subject: [Python-checkins] r83442 - in python/branches/release27-maint: Lib/test/test_bz2.py Misc/NEWS Modules/bz2module.c Message-ID: <20100801201311.4CA36EEAF0@mail.python.org> Author: antoine.pitrou Date: Sun Aug 1 22:13:11 2010 New Revision: 83442 Log: Merged revisions 83440 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83440 | antoine.pitrou | 2010-08-01 22:08:46 +0200 (dim., 01 ao?t 2010) | 4 lines Issue #8397: Raise an error when attempting to mix iteration and regular reads on a BZ2File object, rather than returning incorrect results. ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Lib/test/test_bz2.py python/branches/release27-maint/Misc/NEWS python/branches/release27-maint/Modules/bz2module.c Modified: python/branches/release27-maint/Lib/test/test_bz2.py ============================================================================== --- python/branches/release27-maint/Lib/test/test_bz2.py (original) +++ python/branches/release27-maint/Lib/test/test_bz2.py Sun Aug 1 22:13:11 2010 @@ -329,6 +329,24 @@ finally: f.close() + def testMixedIterationReads(self): + # Issue #8397: mixed iteration and reads should be forbidden. + f = bz2.BZ2File(self.filename, 'wb') + try: + # The internal buffer size is hard-wired to 8192 bytes, we must + # write out more than that for the test to stop half through + # the buffer. + f.write(self.TEXT * 100) + finally: + f.close() + f = bz2.BZ2File(self.filename, 'rb') + try: + next(f) + self.assertRaises(ValueError, f.read) + self.assertRaises(ValueError, f.readline) + self.assertRaises(ValueError, f.readlines) + finally: + f.close() class BZ2CompressorTest(BaseTest): def testCompress(self): Modified: python/branches/release27-maint/Misc/NEWS ============================================================================== --- python/branches/release27-maint/Misc/NEWS (original) +++ python/branches/release27-maint/Misc/NEWS Sun Aug 1 22:13:11 2010 @@ -24,6 +24,9 @@ Library ------- +- Issue #8397: Raise an error when attempting to mix iteration and regular + reads on a BZ2File object, rather than returning incorrect results. + - Issue #5294: Fix the behavior of pdb's "continue" command when called in the top-level debugged frame. Modified: python/branches/release27-maint/Modules/bz2module.c ============================================================================== --- python/branches/release27-maint/Modules/bz2module.c (original) +++ python/branches/release27-maint/Modules/bz2module.c Sun Aug 1 22:13:11 2010 @@ -144,6 +144,22 @@ /* ===================================================================== */ /* Utility functions. */ +/* Refuse regular I/O if there's data in the iteration-buffer. + * Mixing them would cause data to arrive out of order, as the read* + * methods don't use the iteration buffer. */ +static int +check_iterbuffered(BZ2FileObject *f) +{ + if (f->f_buf != NULL && + (f->f_bufend - f->f_bufptr) > 0 && + f->f_buf[0] != '\0') { + PyErr_SetString(PyExc_ValueError, + "Mixing iteration and read methods would lose data"); + return -1; + } + return 0; +} + static int Util_CatchBZ2Error(int bzerror) { @@ -527,6 +543,10 @@ goto cleanup; } + /* refuse to mix with f.next() */ + if (check_iterbuffered(self)) + goto cleanup; + if (bytesrequested < 0) buffersize = Util_NewBufferSize((size_t)0); else @@ -612,6 +632,10 @@ goto cleanup; } + /* refuse to mix with f.next() */ + if (check_iterbuffered(self)) + goto cleanup; + if (sizehint == 0) ret = PyString_FromString(""); else @@ -669,6 +693,10 @@ goto cleanup; } + /* refuse to mix with f.next() */ + if (check_iterbuffered(self)) + goto cleanup; + if ((list = PyList_New(0)) == NULL) goto cleanup; From python-checkins at python.org Sun Aug 1 22:16:12 2010 From: python-checkins at python.org (antoine.pitrou) Date: Sun, 1 Aug 2010 22:16:12 +0200 (CEST) Subject: [Python-checkins] r83443 - in python/branches/release26-maint: Lib/test/test_bz2.py Misc/NEWS Modules/bz2module.c Message-ID: <20100801201612.EABA5EE983@mail.python.org> Author: antoine.pitrou Date: Sun Aug 1 22:16:12 2010 New Revision: 83443 Log: Merged revisions 83442 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/release27-maint ................ r83442 | antoine.pitrou | 2010-08-01 22:13:11 +0200 (dim., 01 ao?t 2010) | 10 lines Merged revisions 83440 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83440 | antoine.pitrou | 2010-08-01 22:08:46 +0200 (dim., 01 ao?t 2010) | 4 lines Issue #8397: Raise an error when attempting to mix iteration and regular reads on a BZ2File object, rather than returning incorrect results. ........ ................ Modified: python/branches/release26-maint/ (props changed) python/branches/release26-maint/Lib/test/test_bz2.py python/branches/release26-maint/Misc/NEWS python/branches/release26-maint/Modules/bz2module.c Modified: python/branches/release26-maint/Lib/test/test_bz2.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_bz2.py (original) +++ python/branches/release26-maint/Lib/test/test_bz2.py Sun Aug 1 22:16:12 2010 @@ -302,6 +302,24 @@ finally: f.close() + def testMixedIterationReads(self): + # Issue #8397: mixed iteration and reads should be forbidden. + f = bz2.BZ2File(self.filename, 'wb') + try: + # The internal buffer size is hard-wired to 8192 bytes, we must + # write out more than that for the test to stop half through + # the buffer. + f.write(self.TEXT * 100) + finally: + f.close() + f = bz2.BZ2File(self.filename, 'rb') + try: + next(f) + self.assertRaises(ValueError, f.read) + self.assertRaises(ValueError, f.readline) + self.assertRaises(ValueError, f.readlines) + finally: + f.close() class BZ2CompressorTest(BaseTest): def testCompress(self): Modified: python/branches/release26-maint/Misc/NEWS ============================================================================== --- python/branches/release26-maint/Misc/NEWS (original) +++ python/branches/release26-maint/Misc/NEWS Sun Aug 1 22:16:12 2010 @@ -84,6 +84,9 @@ Library ------- +- Issue #8397: Raise an error when attempting to mix iteration and regular + reads on a BZ2File object, rather than returning incorrect results. + - Issue #8620: when a Cmd is fed input that reaches EOF without a final newline, it no longer truncates the last character of the last command line. Modified: python/branches/release26-maint/Modules/bz2module.c ============================================================================== --- python/branches/release26-maint/Modules/bz2module.c (original) +++ python/branches/release26-maint/Modules/bz2module.c Sun Aug 1 22:16:12 2010 @@ -144,6 +144,22 @@ /* ===================================================================== */ /* Utility functions. */ +/* Refuse regular I/O if there's data in the iteration-buffer. + * Mixing them would cause data to arrive out of order, as the read* + * methods don't use the iteration buffer. */ +static int +check_iterbuffered(BZ2FileObject *f) +{ + if (f->f_buf != NULL && + (f->f_bufend - f->f_bufptr) > 0 && + f->f_buf[0] != '\0') { + PyErr_SetString(PyExc_ValueError, + "Mixing iteration and read methods would lose data"); + return -1; + } + return 0; +} + static int Util_CatchBZ2Error(int bzerror) { @@ -527,6 +543,10 @@ goto cleanup; } + /* refuse to mix with f.next() */ + if (check_iterbuffered(self)) + goto cleanup; + if (bytesrequested < 0) buffersize = Util_NewBufferSize((size_t)0); else @@ -612,6 +632,10 @@ goto cleanup; } + /* refuse to mix with f.next() */ + if (check_iterbuffered(self)) + goto cleanup; + if (sizehint == 0) ret = PyString_FromString(""); else @@ -669,6 +693,10 @@ goto cleanup; } + /* refuse to mix with f.next() */ + if (check_iterbuffered(self)) + goto cleanup; + if ((list = PyList_New(0)) == NULL) goto cleanup; From python-checkins at python.org Sun Aug 1 22:51:02 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 1 Aug 2010 22:51:02 +0200 (CEST) Subject: [Python-checkins] r83444 - python/branches/py3k/Objects/unicodeobject.c Message-ID: <20100801205102.ED6D2EEAD3@mail.python.org> Author: georg.brandl Date: Sun Aug 1 22:51:02 2010 New Revision: 83444 Log: Revert r83395, it introduces test failures and is not necessary anyway since we now have to nul-terminate the string anyway. Modified: python/branches/py3k/Objects/unicodeobject.c Modified: python/branches/py3k/Objects/unicodeobject.c ============================================================================== --- python/branches/py3k/Objects/unicodeobject.c (original) +++ python/branches/py3k/Objects/unicodeobject.c Sun Aug 1 22:51:02 2010 @@ -3734,7 +3734,7 @@ ch2 = *s++; size--; - if (ch2 >= 0xDC00 && ch2 <= 0xDFFF && size) { + if (ch2 >= 0xDC00 && ch2 <= 0xDFFF) { ucs = (((ch & 0x03FF) << 10) | (ch2 & 0x03FF)) + 0x00010000; *p++ = '\\'; *p++ = 'U'; @@ -3976,7 +3976,7 @@ ch2 = *s++; size--; - if (ch2 >= 0xDC00 && ch2 <= 0xDFFF && size) { + if (ch2 >= 0xDC00 && ch2 <= 0xDFFF) { ucs = (((ch & 0x03FF) << 10) | (ch2 & 0x03FF)) + 0x00010000; *p++ = '\\'; *p++ = 'U'; From python-checkins at python.org Sun Aug 1 22:54:23 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 1 Aug 2010 22:54:23 +0200 (CEST) Subject: [Python-checkins] r83445 - in python/branches/release31-maint: Objects/unicodeobject.c Message-ID: <20100801205423.16ADFEE99A@mail.python.org> Author: georg.brandl Date: Sun Aug 1 22:54:22 2010 New Revision: 83445 Log: Recorded merge of revisions 83444 via svnmerge from svn+ssh://svn.python.org/python/branches/py3k ........ r83444 | georg.brandl | 2010-08-01 22:51:02 +0200 (So, 01 Aug 2010) | 1 line Revert r83395, it introduces test failures and is not necessary anyway since we now have to nul-terminate the string anyway. ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Objects/unicodeobject.c Modified: python/branches/release31-maint/Objects/unicodeobject.c ============================================================================== --- python/branches/release31-maint/Objects/unicodeobject.c (original) +++ python/branches/release31-maint/Objects/unicodeobject.c Sun Aug 1 22:54:22 2010 @@ -3597,7 +3597,7 @@ ch2 = *s++; size--; - if (ch2 >= 0xDC00 && ch2 <= 0xDFFF && size) { + if (ch2 >= 0xDC00 && ch2 <= 0xDFFF) { ucs = (((ch & 0x03FF) << 10) | (ch2 & 0x03FF)) + 0x00010000; *p++ = '\\'; *p++ = 'U'; @@ -3839,7 +3839,7 @@ ch2 = *s++; size--; - if (ch2 >= 0xDC00 && ch2 <= 0xDFFF && size) { + if (ch2 >= 0xDC00 && ch2 <= 0xDFFF) { ucs = (((ch & 0x03FF) << 10) | (ch2 & 0x03FF)) + 0x00010000; *p++ = '\\'; *p++ = 'U'; From python-checkins at python.org Sun Aug 1 22:54:31 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 1 Aug 2010 22:54:31 +0200 (CEST) Subject: [Python-checkins] r83446 - in python/branches/release27-maint: Objects/unicodeobject.c Message-ID: <20100801205431.1878EEEB04@mail.python.org> Author: georg.brandl Date: Sun Aug 1 22:54:30 2010 New Revision: 83446 Log: Recorded merge of revisions 83444 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83444 | georg.brandl | 2010-08-01 22:51:02 +0200 (So, 01 Aug 2010) | 1 line Revert r83395, it introduces test failures and is not necessary anyway since we now have to nul-terminate the string anyway. ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Objects/unicodeobject.c Modified: python/branches/release27-maint/Objects/unicodeobject.c ============================================================================== --- python/branches/release27-maint/Objects/unicodeobject.c (original) +++ python/branches/release27-maint/Objects/unicodeobject.c Sun Aug 1 22:54:30 2010 @@ -294,7 +294,7 @@ } /* We allocate one more byte to make sure the string is - Ux0000 terminated -- XXX is this needed ? + Ux0000 terminated; some code relies on that. XXX This allocator could further be enhanced by assuring that the free list never reduces its size below 1. @@ -3067,7 +3067,7 @@ ch2 = *s++; size--; - if (ch2 >= 0xDC00 && ch2 <= 0xDFFF && size) { + if (ch2 >= 0xDC00 && ch2 <= 0xDFFF) { ucs = (((ch & 0x03FF) << 10) | (ch2 & 0x03FF)) + 0x00010000; *p++ = '\\'; *p++ = 'U'; @@ -3316,7 +3316,7 @@ ch2 = *s++; size--; - if (ch2 >= 0xDC00 && ch2 <= 0xDFFF && size) { + if (ch2 >= 0xDC00 && ch2 <= 0xDFFF) { ucs = (((ch & 0x03FF) << 10) | (ch2 & 0x03FF)) + 0x00010000; *p++ = '\\'; *p++ = 'U'; From python-checkins at python.org Sun Aug 1 22:55:37 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 1 Aug 2010 22:55:37 +0200 (CEST) Subject: [Python-checkins] r83447 - in python/branches/release31-maint: Lib/test/test_posix.py Message-ID: <20100801205537.79CCAEE99A@mail.python.org> Author: georg.brandl Date: Sun Aug 1 22:55:37 2010 New Revision: 83447 Log: Merged revisions 83431 via svnmerge from svn+ssh://svn.python.org/python/branches/py3k ........ r83431 | ronald.oussoren | 2010-08-01 21:18:13 +0200 (So, 01 Aug 2010) | 6 lines test_getgroups as introduced with issue7900 failed on systems where 'id -G' and posix.getgroups() returned the same information, but one of the sources contains duplicate information. Rewrite the check using sets instead of lists. ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Lib/test/test_posix.py Modified: python/branches/release31-maint/Lib/test/test_posix.py ============================================================================== --- python/branches/release31-maint/Lib/test/test_posix.py (original) +++ python/branches/release31-maint/Lib/test/test_posix.py Sun Aug 1 22:55:37 2010 @@ -281,11 +281,11 @@ if not groups: raise unittest.SkipTest("need working 'id -G'") - # The order of groups isn't important, hence the calls - # to sorted. + # 'id -G' and 'os.getgroups()' should return the same + # groups, ignoring order and duplicates. self.assertEqual( - list(sorted([int(x) for x in groups.split()])), - list(sorted(posix.getgroups()))) + set([int(x) for x in groups.split()]), + set(posix.getgroups())) class PosixGroupsTester(unittest.TestCase): From python-checkins at python.org Sun Aug 1 22:57:27 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 1 Aug 2010 22:57:27 +0200 (CEST) Subject: [Python-checkins] r83448 - in python/branches/release31-maint: Lib/test/test_robotparser.py Lib/urllib/robotparser.py Misc/NEWS Message-ID: <20100801205727.A2E49EE99A@mail.python.org> Author: georg.brandl Date: Sun Aug 1 22:57:27 2010 New Revision: 83448 Log: Merged revisions 83238 via svnmerge from svn+ssh://svn.python.org/python/branches/py3k ........ r83238 | georg.brandl | 2010-07-29 19:55:01 +0200 (Do, 29 Jul 2010) | 1 line #4108: the first default entry (User-agent: *) wins. ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Lib/test/test_robotparser.py python/branches/release31-maint/Lib/urllib/robotparser.py python/branches/release31-maint/Misc/NEWS Modified: python/branches/release31-maint/Lib/test/test_robotparser.py ============================================================================== --- python/branches/release31-maint/Lib/test/test_robotparser.py (original) +++ python/branches/release31-maint/Lib/test/test_robotparser.py Sun Aug 1 22:57:27 2010 @@ -216,6 +216,20 @@ RobotTest(14, doc, good, bad) +# 15. For issue #4108 (obey first * entry) +doc = """ +User-agent: * +Disallow: /some/path + +User-agent: * +Disallow: /another/path +""" + +good = ['/another/path'] +bad = ['/some/path'] + +RobotTest(15, doc, good, bad) + class NetworkTestCase(unittest.TestCase): Modified: python/branches/release31-maint/Lib/urllib/robotparser.py ============================================================================== --- python/branches/release31-maint/Lib/urllib/robotparser.py (original) +++ python/branches/release31-maint/Lib/urllib/robotparser.py Sun Aug 1 22:57:27 2010 @@ -66,7 +66,9 @@ def _add_entry(self, entry): if "*" in entry.useragents: # the default entry is considered last - self.default_entry = entry + if self.default_entry is None: + # the first default entry wins + self.default_entry = entry else: self.entries.append(entry) @@ -118,7 +120,7 @@ entry.rulelines.append(RuleLine(line[1], True)) state = 2 if state == 2: - self.entries.append(entry) + self._add_entry(entry) def can_fetch(self, useragent, url): Modified: python/branches/release31-maint/Misc/NEWS ============================================================================== --- python/branches/release31-maint/Misc/NEWS (original) +++ python/branches/release31-maint/Misc/NEWS Sun Aug 1 22:57:27 2010 @@ -84,6 +84,9 @@ Library ------- +- Issue #4108: In urllib.robotparser, if there are multiple 'User-agent: *' + entries, consider the first one. + - Issue #8397: Raise an error when attempting to mix iteration and regular reads on a BZ2File object, rather than returning incorrect results. From python-checkins at python.org Sun Aug 1 22:59:03 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 1 Aug 2010 22:59:03 +0200 (CEST) Subject: [Python-checkins] r83449 - in python/branches/release27-maint: Lib/robotparser.py Lib/test/test_robotparser.py Misc/NEWS Message-ID: <20100801205903.EE16DEE99A@mail.python.org> Author: georg.brandl Date: Sun Aug 1 22:59:03 2010 New Revision: 83449 Log: Merged revisions 83238 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83238 | georg.brandl | 2010-07-29 19:55:01 +0200 (Do, 29 Jul 2010) | 1 line #4108: the first default entry (User-agent: *) wins. ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Lib/robotparser.py python/branches/release27-maint/Lib/test/test_robotparser.py python/branches/release27-maint/Misc/NEWS Modified: python/branches/release27-maint/Lib/robotparser.py ============================================================================== --- python/branches/release27-maint/Lib/robotparser.py (original) +++ python/branches/release27-maint/Lib/robotparser.py Sun Aug 1 22:59:03 2010 @@ -68,7 +68,9 @@ def _add_entry(self, entry): if "*" in entry.useragents: # the default entry is considered last - self.default_entry = entry + if self.default_entry is None: + # the first default entry wins + self.default_entry = entry else: self.entries.append(entry) @@ -120,7 +122,7 @@ entry.rulelines.append(RuleLine(line[1], True)) state = 2 if state == 2: - self.entries.append(entry) + self._add_entry(entry) def can_fetch(self, useragent, url): Modified: python/branches/release27-maint/Lib/test/test_robotparser.py ============================================================================== --- python/branches/release27-maint/Lib/test/test_robotparser.py (original) +++ python/branches/release27-maint/Lib/test/test_robotparser.py Sun Aug 1 22:59:03 2010 @@ -213,6 +213,20 @@ RobotTest(14, doc, good, bad) +# 15. For issue #4108 (obey first * entry) +doc = """ +User-agent: * +Disallow: /some/path + +User-agent: * +Disallow: /another/path +""" + +good = ['/another/path'] +bad = ['/some/path'] + +RobotTest(15, doc, good, bad) + class NetworkTestCase(unittest.TestCase): Modified: python/branches/release27-maint/Misc/NEWS ============================================================================== --- python/branches/release27-maint/Misc/NEWS (original) +++ python/branches/release27-maint/Misc/NEWS Sun Aug 1 22:59:03 2010 @@ -24,6 +24,9 @@ Library ------- +- Issue #4108: In urllib.robotparser, if there are multiple 'User-agent: *' + entries, consider the first one. + - Issue #8397: Raise an error when attempting to mix iteration and regular reads on a BZ2File object, rather than returning incorrect results. From python-checkins at python.org Sun Aug 1 23:00:43 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 1 Aug 2010 23:00:43 +0200 (CEST) Subject: [Python-checkins] r83450 - python/branches/release27-maint Message-ID: <20100801210043.2EFBEEE99A@mail.python.org> Author: georg.brandl Date: Sun Aug 1 23:00:43 2010 New Revision: 83450 Log: Blocked revisions 83231,83236 via svnmerge ........ r83231 | georg.brandl | 2010-07-29 16:46:07 +0200 (Do, 29 Jul 2010) | 1 line #9397: remove mention of dbm.bsd which does not exist anymore. ........ r83236 | georg.brandl | 2010-07-29 19:16:10 +0200 (Do, 29 Jul 2010) | 1 line #6630: allow customizing flags for compiling string.Template.idpattern. ........ Modified: python/branches/release27-maint/ (props changed) From python-checkins at python.org Sun Aug 1 23:03:01 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 1 Aug 2010 23:03:01 +0200 (CEST) Subject: [Python-checkins] r83451 - in python/branches/release31-maint: Doc/library/configparser.rst Doc/library/dbm.rst Doc/library/itertools.rst Doc/library/locale.rst Lib/configparser.py Objects/unicodeobject.c Message-ID: <20100801210301.EBB88EE99A@mail.python.org> Author: georg.brandl Date: Sun Aug 1 23:03:01 2010 New Revision: 83451 Log: Merged revisions 83226-83227,83229-83232 via svnmerge from svn+ssh://svn.python.org/python/branches/py3k ........ r83226 | georg.brandl | 2010-07-29 16:17:12 +0200 (Do, 29 Jul 2010) | 1 line #1090076: explain the behavior of *vars* in get() better. ........ r83227 | georg.brandl | 2010-07-29 16:23:06 +0200 (Do, 29 Jul 2010) | 1 line Use Py_CLEAR(). ........ r83229 | georg.brandl | 2010-07-29 16:32:22 +0200 (Do, 29 Jul 2010) | 1 line #9407: document configparser.Error. ........ r83230 | georg.brandl | 2010-07-29 16:36:11 +0200 (Do, 29 Jul 2010) | 1 line Use correct directive and name. ........ r83231 | georg.brandl | 2010-07-29 16:46:07 +0200 (Do, 29 Jul 2010) | 1 line #9397: remove mention of dbm.bsd which does not exist anymore. ........ r83232 | georg.brandl | 2010-07-29 16:49:08 +0200 (Do, 29 Jul 2010) | 1 line #9388: remove ERA_YEAR which is never defined in the source code. ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Doc/library/configparser.rst python/branches/release31-maint/Doc/library/dbm.rst python/branches/release31-maint/Doc/library/itertools.rst python/branches/release31-maint/Doc/library/locale.rst python/branches/release31-maint/Lib/configparser.py python/branches/release31-maint/Objects/unicodeobject.c Modified: python/branches/release31-maint/Doc/library/configparser.rst ============================================================================== --- python/branches/release31-maint/Doc/library/configparser.rst (original) +++ python/branches/release31-maint/Doc/library/configparser.rst Sun Aug 1 23:03:01 2010 @@ -35,6 +35,18 @@ provided on initialization and retrieval. Lines beginning with ``'#'`` or ``';'`` are ignored and may be used to provide comments. +Configuration files may include comments, prefixed by specific characters (``#`` +and ``;``). Comments may appear on their own in an otherwise empty line, or may +be entered in lines holding values or spection names. In the latter case, they +need to be preceded by a whitespace character to be recognized as a comment. +(For backwards compatibility, only ``;`` starts an inline comment, while ``#`` +does not.) + +On top of the core functionality, :class:`SafeConfigParser` supports +interpolation. This means values can contain format strings which refer to +other values in the same section, or values in a special ``DEFAULT`` section. +Additional defaults can be provided on initialization. + For example:: [My Section] @@ -100,6 +112,11 @@ The default *dict_type* is :class:`collections.OrderedDict`. +.. exception:: Error + + Base class for all other configparser exceptions. + + .. exception:: NoSectionError Exception raised when a specified section is not found. @@ -331,10 +348,13 @@ .. method:: ConfigParser.get(section, option, raw=False, vars=None) - Get an *option* value for the named *section*. All the ``'%'`` interpolations - are expanded in the return values, based on the defaults passed into the - constructor, as well as the options *vars* provided, unless the *raw* argument - is true. + Get an *option* value for the named *section*. If *vars* is provided, it + must be a dictionary. The *option* is looked up in *vars* (if provided), + *section*, and in *defaults* in that order. + + All the ``'%'`` interpolations are expanded in the return values, unless the + *raw* argument is true. Values for interpolation keys are looked up in the + same manner as the option. .. method:: ConfigParser.items(section, raw=False, vars=None) Modified: python/branches/release31-maint/Doc/library/dbm.rst ============================================================================== --- python/branches/release31-maint/Doc/library/dbm.rst (original) +++ python/branches/release31-maint/Doc/library/dbm.rst Sun Aug 1 23:03:01 2010 @@ -21,8 +21,8 @@ .. function:: whichdb(filename) This functionattempts to guess which of the several simple database modules - available --- :mod:`dbm.bsd`, :mod:`dbm.gnu`, :mod:`dbm.ndbm` or - :mod:`dbm.dumb` --- should be used to open a given file. + available --- :mod:`dbm.gnu`, :mod:`dbm.ndbm` or :mod:`dbm.dumb` --- should + be used to open a given file. Returns one of the following values: ``None`` if the file can't be opened because it's unreadable or doesn't exist; the empty string (``''``) if the @@ -227,10 +227,9 @@ always stored as bytes. Printing a ``dbm`` object doesn't print the keys and values, and the :meth:`items` and :meth:`values` methods are not supported. -This module can be used with the "classic" ndbm interface, the BSD DB -compatibility interface, or the GNU GDBM compatibility interface. On Unix, the -:program:`configure` script will attempt to locate the appropriate header file -to simplify building this module. +This module can be used with the "classic" ndbm interface or the GNU GDBM +compatibility interface. On Unix, the :program:`configure` script will attempt +to locate the appropriate header file to simplify building this module. .. exception:: error @@ -246,9 +245,7 @@ .. function:: open(filename[, flag[, mode]]) Open a dbm database and return a ``dbm`` object. The *filename* argument is the - name of the database file (without the :file:`.dir` or :file:`.pag` extensions; - note that the BSD DB implementation of the interface will append the extension - :file:`.db` and only create one file). + name of the database file (without the :file:`.dir` or :file:`.pag` extensions). The optional *flag* argument must be one of these values: Modified: python/branches/release31-maint/Doc/library/itertools.rst ============================================================================== --- python/branches/release31-maint/Doc/library/itertools.rst (original) +++ python/branches/release31-maint/Doc/library/itertools.rst Sun Aug 1 23:03:01 2010 @@ -99,7 +99,7 @@ yield element -.. function:: itertools.chain.from_iterable(iterable) +.. classmethod:: chain.from_iterable(iterable) Alternate constructor for :func:`chain`. Gets chained inputs from a single iterable argument that is evaluated lazily. Equivalent to:: Modified: python/branches/release31-maint/Doc/library/locale.rst ============================================================================== --- python/branches/release31-maint/Doc/library/locale.rst (original) +++ python/branches/release31-maint/Doc/library/locale.rst Sun Aug 1 23:03:01 2010 @@ -244,10 +244,6 @@ specified, and therefore you should not assume knowledge of it on different systems. - .. data:: ERA_YEAR - - Get the year in the relevant era of the locale. - .. data:: ERA_D_T_FMT Get a format string for :func:`strftime` to represent dates and times in a Modified: python/branches/release31-maint/Lib/configparser.py ============================================================================== --- python/branches/release31-maint/Lib/configparser.py (original) +++ python/branches/release31-maint/Lib/configparser.py Sun Aug 1 23:03:01 2010 @@ -521,11 +521,12 @@ def get(self, section, option, raw=False, vars=None): """Get an option value for a given section. - All % interpolations are expanded in the return values, based on the - defaults passed into the constructor, unless the optional argument - `raw' is true. Additional substitutions may be provided using the - `vars' argument, which must be a dictionary whose contents overrides - any pre-existing defaults. + If `vars' is provided, it must be a dictionary. The option is looked up + in `vars' (if provided), `section', and in `defaults' in that order. + + All % interpolations are expanded in the return values, unless the + optional argument `raw' is true. Values for interpolation keys are + looked up in the same manner as the option. The section DEFAULT is special. """ Modified: python/branches/release31-maint/Objects/unicodeobject.c ============================================================================== --- python/branches/release31-maint/Objects/unicodeobject.c (original) +++ python/branches/release31-maint/Objects/unicodeobject.c Sun Aug 1 23:03:01 2010 @@ -294,8 +294,7 @@ reset: /* Reset the object caches */ if (unicode->defenc) { - Py_DECREF(unicode->defenc); - unicode->defenc = NULL; + Py_CLEAR(unicode->defenc); } unicode->hash = -1; @@ -414,8 +413,7 @@ unicode->length = 0; } if (unicode->defenc) { - Py_DECREF(unicode->defenc); - unicode->defenc = NULL; + Py_CLEAR(unicode->defenc); } /* Add to free list */ *(PyUnicodeObject **)unicode = free_list; From python-checkins at python.org Sun Aug 1 23:06:46 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 1 Aug 2010 23:06:46 +0200 (CEST) Subject: [Python-checkins] r83452 - in python/branches/release27-maint: Doc/library/configparser.rst Doc/library/itertools.rst Doc/library/locale.rst Lib/ConfigParser.py Objects/unicodeobject.c Message-ID: <20100801210646.92C75EE983@mail.python.org> Author: georg.brandl Date: Sun Aug 1 23:06:46 2010 New Revision: 83452 Log: Merged revisions 83226-83227,83229-83230,83232 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83226 | georg.brandl | 2010-07-29 16:17:12 +0200 (Do, 29 Jul 2010) | 1 line #1090076: explain the behavior of *vars* in get() better. ........ r83227 | georg.brandl | 2010-07-29 16:23:06 +0200 (Do, 29 Jul 2010) | 1 line Use Py_CLEAR(). ........ r83229 | georg.brandl | 2010-07-29 16:32:22 +0200 (Do, 29 Jul 2010) | 1 line #9407: document configparser.Error. ........ r83230 | georg.brandl | 2010-07-29 16:36:11 +0200 (Do, 29 Jul 2010) | 1 line Use correct directive and name. ........ r83232 | georg.brandl | 2010-07-29 16:49:08 +0200 (Do, 29 Jul 2010) | 1 line #9388: remove ERA_YEAR which is never defined in the source code. ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Doc/library/configparser.rst python/branches/release27-maint/Doc/library/itertools.rst python/branches/release27-maint/Doc/library/locale.rst python/branches/release27-maint/Lib/ConfigParser.py python/branches/release27-maint/Objects/unicodeobject.c Modified: python/branches/release27-maint/Doc/library/configparser.rst ============================================================================== --- python/branches/release27-maint/Doc/library/configparser.rst (original) +++ python/branches/release27-maint/Doc/library/configparser.rst Sun Aug 1 23:06:46 2010 @@ -41,6 +41,18 @@ provided on initialization and retrieval. Lines beginning with ``'#'`` or ``';'`` are ignored and may be used to provide comments. +Configuration files may include comments, prefixed by specific characters (``#`` +and ``;``). Comments may appear on their own in an otherwise empty line, or may +be entered in lines holding values or spection names. In the latter case, they +need to be preceded by a whitespace character to be recognized as a comment. +(For backwards compatibility, only ``;`` starts an inline comment, while ``#`` +does not.) + +On top of the core functionality, :class:`SafeConfigParser` supports +interpolation. This means values can contain format strings which refer to +other values in the same section, or values in a special ``DEFAULT`` section. +Additional defaults can be provided on initialization. + For example:: [My Section] @@ -128,6 +140,11 @@ *allow_no_value* was added. +.. exception:: Error + + Base class for all other configparser exceptions. + + .. exception:: NoSectionError Exception raised when a specified section is not found. @@ -371,11 +388,13 @@ .. method:: ConfigParser.get(section, option[, raw[, vars]]) - Get an *option* value for the named *section*. All the ``'%'`` interpolations - are expanded in the return values, based on the defaults passed into the - constructor, as well as the options *vars* provided, unless the *raw* argument - is true. - + Get an *option* value for the named *section*. If *vars* is provided, it + must be a dictionary. The *option* is looked up in *vars* (if provided), + *section*, and in *defaults* in that order. + + All the ``'%'`` interpolations are expanded in the return values, unless the + *raw* argument is true. Values for interpolation keys are looked up in the + same manner as the option. .. method:: ConfigParser.items(section[, raw[, vars]]) Modified: python/branches/release27-maint/Doc/library/itertools.rst ============================================================================== --- python/branches/release27-maint/Doc/library/itertools.rst (original) +++ python/branches/release27-maint/Doc/library/itertools.rst Sun Aug 1 23:06:46 2010 @@ -104,7 +104,7 @@ yield element -.. function:: itertools.chain.from_iterable(iterable) +.. classmethod:: chain.from_iterable(iterable) Alternate constructor for :func:`chain`. Gets chained inputs from a single iterable argument that is evaluated lazily. Equivalent to:: Modified: python/branches/release27-maint/Doc/library/locale.rst ============================================================================== --- python/branches/release27-maint/Doc/library/locale.rst (original) +++ python/branches/release27-maint/Doc/library/locale.rst Sun Aug 1 23:06:46 2010 @@ -248,10 +248,6 @@ specified, and therefore you should not assume knowledge of it on different systems. - .. data:: ERA_YEAR - - Get the year in the relevant era of the locale. - .. data:: ERA_D_T_FMT Get a format string for :func:`strftime` to represent dates and times in a Modified: python/branches/release27-maint/Lib/ConfigParser.py ============================================================================== --- python/branches/release27-maint/Lib/ConfigParser.py (original) +++ python/branches/release27-maint/Lib/ConfigParser.py Sun Aug 1 23:06:46 2010 @@ -550,11 +550,12 @@ def get(self, section, option, raw=False, vars=None): """Get an option value for a given section. - All % interpolations are expanded in the return values, based on the - defaults passed into the constructor, unless the optional argument - `raw' is true. Additional substitutions may be provided using the - `vars' argument, which must be a dictionary whose contents overrides - any pre-existing defaults. + If `vars' is provided, it must be a dictionary. The option is looked up + in `vars' (if provided), `section', and in `defaults' in that order. + + All % interpolations are expanded in the return values, unless the + optional argument `raw' is true. Values for interpolation keys are + looked up in the same manner as the option. The section DEFAULT is special. """ Modified: python/branches/release27-maint/Objects/unicodeobject.c ============================================================================== --- python/branches/release27-maint/Objects/unicodeobject.c (original) +++ python/branches/release27-maint/Objects/unicodeobject.c Sun Aug 1 23:06:46 2010 @@ -285,8 +285,7 @@ reset: /* Reset the object caches */ if (unicode->defenc) { - Py_DECREF(unicode->defenc); - unicode->defenc = NULL; + Py_CLEAR(unicode->defenc); } unicode->hash = -1; @@ -384,8 +383,7 @@ unicode->length = 0; } if (unicode->defenc) { - Py_DECREF(unicode->defenc); - unicode->defenc = NULL; + Py_CLEAR(unicode->defenc); } /* Add to free list */ *(PyUnicodeObject **)unicode = free_list; From python-checkins at python.org Sun Aug 1 23:08:39 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 1 Aug 2010 23:08:39 +0200 (CEST) Subject: [Python-checkins] r83453 - python/branches/release31-maint Message-ID: <20100801210839.E7C13EE983@mail.python.org> Author: georg.brandl Date: Sun Aug 1 23:08:39 2010 New Revision: 83453 Log: Blocked revisions 83236 via svnmerge ........ r83236 | georg.brandl | 2010-07-29 19:16:10 +0200 (Do, 29 Jul 2010) | 1 line #6630: allow customizing flags for compiling string.Template.idpattern. ........ Modified: python/branches/release31-maint/ (props changed) From python-checkins at python.org Sun Aug 1 23:09:16 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 1 Aug 2010 23:09:16 +0200 (CEST) Subject: [Python-checkins] r83454 - in python/branches/release31-maint: Doc/library/re.rst Message-ID: <20100801210916.BD804EE983@mail.python.org> Author: georg.brandl Date: Sun Aug 1 23:09:16 2010 New Revision: 83454 Log: Merged revisions 83217 via svnmerge from svn+ssh://svn.python.org/python/branches/py3k ........ r83217 | georg.brandl | 2010-07-29 13:15:36 +0200 (Do, 29 Jul 2010) | 1 line Remove Python 1.5 compatibility note. ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Doc/library/re.rst Modified: python/branches/release31-maint/Doc/library/re.rst ============================================================================== --- python/branches/release31-maint/Doc/library/re.rst (original) +++ python/branches/release31-maint/Doc/library/re.rst Sun Aug 1 23:09:16 2010 @@ -485,7 +485,7 @@ Note that for backward compatibility, the :const:`re.U` flag still exists (as well as its synonym :const:`re.UNICODE` and its embedded - counterpart ``(?u)``), but these are redundant in Python 3.0 since + counterpart ``(?u)``), but these are redundant in Python 3 since matches are Unicode by default for strings (and Unicode matching isn't allowed for bytes). @@ -504,7 +504,7 @@ Make ``\w``, ``\W``, ``\b``, ``\B``, ``\s`` and ``\S`` dependent on the current locale. The use of this flag is discouraged as the locale mechanism is very unreliable, and it only handles one "culture" at a time anyway; - you should use Unicode matching instead, which is the default in Python 3.0 + you should use Unicode matching instead, which is the default in Python 3 for Unicode (str) patterns. @@ -888,10 +888,7 @@ Return a tuple containing all the subgroups of the match, from 1 up to however many groups are in the pattern. The *default* argument is used for groups that - did not participate in the match; it defaults to ``None``. (Incompatibility - note: in the original Python 1.5 release, if the tuple was one element long, a - string would be returned instead. In later versions (from 1.5.1 on), a - singleton tuple is returned in such cases.) + did not participate in the match; it defaults to ``None``. For example: @@ -1113,7 +1110,7 @@ >>> re.match('Begin (\w| )*? end', s).end() Traceback (most recent call last): File "", line 1, in ? - File "/usr/local/lib/python2.5/re.py", line 132, in match + File "/usr/local/lib/python3.1/re.py", line 132, in match return _compile(pattern, flags).match(string) RuntimeError: maximum recursion limit exceeded From python-checkins at python.org Sun Aug 1 23:09:54 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 1 Aug 2010 23:09:54 +0200 (CEST) Subject: [Python-checkins] r83455 - in python/branches/release31-maint: Doc/library/html.parser.rst Message-ID: <20100801210954.F38A5EE983@mail.python.org> Author: georg.brandl Date: Sun Aug 1 23:09:54 2010 New Revision: 83455 Log: Merged revisions 83223 via svnmerge from svn+ssh://svn.python.org/python/branches/py3k ........ r83223 | georg.brandl | 2010-07-29 15:38:37 +0200 (Do, 29 Jul 2010) | 1 line #3874: document HTMLParser.unknown_decl(). ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Doc/library/html.parser.rst Modified: python/branches/release31-maint/Doc/library/html.parser.rst ============================================================================== --- python/branches/release31-maint/Doc/library/html.parser.rst (original) +++ python/branches/release31-maint/Doc/library/html.parser.rst Sun Aug 1 23:09:54 2010 @@ -134,10 +134,18 @@ .. method:: HTMLParser.handle_decl(decl) - Method called when an SGML declaration is read by the parser. The *decl* - parameter will be the entire contents of the declaration inside the ```` markup. It is intended to be overridden by a derived class; the base - class implementation does nothing. + Method called when an SGML ``doctype`` declaration is read by the parser. + The *decl* parameter will be the entire contents of the declaration inside + the ```` markup. It is intended to be overridden by a derived class; + the base class implementation does nothing. + + +.. method:: HTMLParser.unknown_decl(data) + + Method called when an unrecognized SGML declaration is read by the parser. + The *data* parameter will be the entire contents of the declaration inside + the ```` markup. It is sometimes useful to be be overridden by a + derived class; the base class implementation throws an :exc:`HTMLParseError`. .. method:: HTMLParser.handle_pi(data) From python-checkins at python.org Sun Aug 1 23:10:36 2010 From: python-checkins at python.org (raymond.hettinger) Date: Sun, 1 Aug 2010 23:10:36 +0200 (CEST) Subject: [Python-checkins] r83456 - python/branches/py3k/Modules/posixmodule.c Message-ID: <20100801211036.01FA2EE992@mail.python.org> Author: raymond.hettinger Date: Sun Aug 1 23:10:35 2010 New Revision: 83456 Log: Issue 9445: Fix undefined symbols on VS8.0 build. Modified: python/branches/py3k/Modules/posixmodule.c Modified: python/branches/py3k/Modules/posixmodule.c ============================================================================== --- python/branches/py3k/Modules/posixmodule.c (original) +++ python/branches/py3k/Modules/posixmodule.c Sun Aug 1 23:10:35 2010 @@ -262,6 +262,15 @@ #ifdef HAVE_PROCESS_H #include #endif +#ifndef VOLUME_NAME_DOS +#define VOLUME_NAME_DOS 0x0 +#endif +#ifndef VOLUME_NAME_NT +#define VOLUME_NAME_NT 0x2 +#endif +#ifndef IO_REPARSE_TAG_SYMLINK +#define IO_REPARSE_TAG_SYMLINK (0xA000000CL) +#endif #include "osdefs.h" #include #include From python-checkins at python.org Sun Aug 1 23:10:57 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 1 Aug 2010 23:10:57 +0200 (CEST) Subject: [Python-checkins] r83457 - in python/branches/release27-maint: Doc/library/htmlparser.rst Message-ID: <20100801211057.D988CEE983@mail.python.org> Author: georg.brandl Date: Sun Aug 1 23:10:57 2010 New Revision: 83457 Log: Merged revisions 83223 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83223 | georg.brandl | 2010-07-29 15:38:37 +0200 (Do, 29 Jul 2010) | 1 line #3874: document HTMLParser.unknown_decl(). ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Doc/library/htmlparser.rst Modified: python/branches/release27-maint/Doc/library/htmlparser.rst ============================================================================== --- python/branches/release27-maint/Doc/library/htmlparser.rst (original) +++ python/branches/release27-maint/Doc/library/htmlparser.rst Sun Aug 1 23:10:57 2010 @@ -148,10 +148,18 @@ .. method:: HTMLParser.handle_decl(decl) - Method called when an SGML declaration is read by the parser. The *decl* - parameter will be the entire contents of the declaration inside the ```` markup. It is intended to be overridden by a derived class; the base - class implementation does nothing. + Method called when an SGML ``doctype`` declaration is read by the parser. + The *decl* parameter will be the entire contents of the declaration inside + the ```` markup. It is intended to be overridden by a derived class; + the base class implementation does nothing. + + +.. method:: HTMLParser.unknown_decl(data) + + Method called when an unrecognized SGML declaration is read by the parser. + The *data* parameter will be the entire contents of the declaration inside + the ```` markup. It is sometimes useful to be be overridden by a + derived class; the base class implementation throws an :exc:`HTMLParseError`. .. method:: HTMLParser.handle_pi(data) From python-checkins at python.org Sun Aug 1 23:11:59 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 1 Aug 2010 23:11:59 +0200 (CEST) Subject: [Python-checkins] r83458 - python/branches/release31-maint Message-ID: <20100801211159.29E2AEEA62@mail.python.org> Author: georg.brandl Date: Sun Aug 1 23:11:59 2010 New Revision: 83458 Log: Blocked revisions 83202,83213,83218-83220,83222 via svnmerge ........ r83202 | georg.brandl | 2010-07-28 15:13:46 +0200 (Mi, 28 Jul 2010) | 1 line #1682942: add some ConfigParser features: alternate delimiters, alternate comments, empty lines in values. Also enhance the docs with more examples and mention SafeConfigParser before ConfigParser. Patch by Lukas Langa, review by myself, Eric and Ezio. ........ r83213 | georg.brandl | 2010-07-28 19:37:27 +0200 (Mi, 28 Jul 2010) | 1 line Add missing file. ........ r83218 | georg.brandl | 2010-07-29 13:49:05 +0200 (Do, 29 Jul 2010) | 1 line #6538: fix regex documentation again -- use fictional class names "regex" and "match" but do not document them as classes, remove 1.5 compat info and use new default argument syntax where possible. ........ r83219 | georg.brandl | 2010-07-29 13:56:20 +0200 (Do, 29 Jul 2010) | 1 line Fix for r83202: improve the handling of empty lines. ........ r83220 | georg.brandl | 2010-07-29 14:17:40 +0200 (Do, 29 Jul 2010) | 1 line #9411: allow selecting an encoding for configparser files. Also adds a new test config file to test special cases. ........ r83222 | georg.brandl | 2010-07-29 15:19:42 +0200 (Do, 29 Jul 2010) | 1 line Fix #9412: make list of messages an instance attribute instead of class attribute. ........ Modified: python/branches/release31-maint/ (props changed) From python-checkins at python.org Sun Aug 1 23:12:01 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 1 Aug 2010 23:12:01 +0200 (CEST) Subject: [Python-checkins] r83459 - python/branches/release27-maint Message-ID: <20100801211201.1CD0EEEA47@mail.python.org> Author: georg.brandl Date: Sun Aug 1 23:12:00 2010 New Revision: 83459 Log: Blocked revisions 83202,83213,83217-83220,83222 via svnmerge ........ r83202 | georg.brandl | 2010-07-28 15:13:46 +0200 (Mi, 28 Jul 2010) | 1 line #1682942: add some ConfigParser features: alternate delimiters, alternate comments, empty lines in values. Also enhance the docs with more examples and mention SafeConfigParser before ConfigParser. Patch by Lukas Langa, review by myself, Eric and Ezio. ........ r83213 | georg.brandl | 2010-07-28 19:37:27 +0200 (Mi, 28 Jul 2010) | 1 line Add missing file. ........ r83217 | georg.brandl | 2010-07-29 13:15:36 +0200 (Do, 29 Jul 2010) | 1 line Remove Python 1.5 compatibility note. ........ r83218 | georg.brandl | 2010-07-29 13:49:05 +0200 (Do, 29 Jul 2010) | 1 line #6538: fix regex documentation again -- use fictional class names "regex" and "match" but do not document them as classes, remove 1.5 compat info and use new default argument syntax where possible. ........ r83219 | georg.brandl | 2010-07-29 13:56:20 +0200 (Do, 29 Jul 2010) | 1 line Fix for r83202: improve the handling of empty lines. ........ r83220 | georg.brandl | 2010-07-29 14:17:40 +0200 (Do, 29 Jul 2010) | 1 line #9411: allow selecting an encoding for configparser files. Also adds a new test config file to test special cases. ........ r83222 | georg.brandl | 2010-07-29 15:19:42 +0200 (Do, 29 Jul 2010) | 1 line Fix #9412: make list of messages an instance attribute instead of class attribute. ........ Modified: python/branches/release27-maint/ (props changed) From python-checkins at python.org Sun Aug 1 23:16:54 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 1 Aug 2010 23:16:54 +0200 (CEST) Subject: [Python-checkins] r83460 - in python/branches/release31-maint: Lib/asyncore.py Lib/test/test_asyncore.py Misc/ACKS Misc/NEWS Message-ID: <20100801211654.7DFB0EE992@mail.python.org> Author: georg.brandl Date: Sun Aug 1 23:16:54 2010 New Revision: 83460 Log: Merged revisions 83201 via svnmerge from svn+ssh://svn.python.org/python/branches/py3k ........ r83201 | georg.brandl | 2010-07-28 10:19:35 +0200 (Mi, 28 Jul 2010) | 1 line #9354: Provide getsockopt() in asyncore file_wrapper(). Patch by Lukas Langa. ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Lib/asyncore.py python/branches/release31-maint/Lib/test/test_asyncore.py python/branches/release31-maint/Misc/ACKS python/branches/release31-maint/Misc/NEWS Modified: python/branches/release31-maint/Lib/asyncore.py ============================================================================== --- python/branches/release31-maint/Lib/asyncore.py (original) +++ python/branches/release31-maint/Lib/asyncore.py Sun Aug 1 23:16:54 2010 @@ -598,6 +598,14 @@ def send(self, *args): return os.write(self.fd, *args) + def getsockopt(self, level, optname, buflen=None): + if (level == socket.SOL_SOCKET and + optname == socket.SO_ERROR and + not buflen): + return 0 + raise NotImplementedError("Only asyncore specific behaviour " + "implemented.") + read = recv write = send Modified: python/branches/release31-maint/Lib/test/test_asyncore.py ============================================================================== --- python/branches/release31-maint/Lib/test/test_asyncore.py (original) +++ python/branches/release31-maint/Lib/test/test_asyncore.py Sun Aug 1 23:16:54 2010 @@ -413,6 +413,19 @@ w.close() self.assertEqual(open(TESTFN, 'rb').read(), self.d + d1 + d2) + @unittest.skipUnless(hasattr(asyncore, 'file_dispatcher'), + ' asyncore.file_dispatcher required') + def test_dispatcher(self): + fd = os.open(TESTFN, os.O_RDONLY) + data = [] + class FileDispatcher(asyncore.file_dispatcher): + def handle_read(self): + data.append(self.recv(29)) + s = FileDispatcher(fd) + os.close(fd) + asyncore.loop(timeout=0.01, use_poll=True, count=2) + self.assertEqual(b"".join(data), self.d) + def test_main(): tests = [HelperFunctionTests, DispatcherTests, DispatcherWithSendTests, Modified: python/branches/release31-maint/Misc/ACKS ============================================================================== --- python/branches/release31-maint/Misc/ACKS (original) +++ python/branches/release31-maint/Misc/ACKS Sun Aug 1 23:16:54 2010 @@ -426,6 +426,7 @@ Andrew Kuchling Vladimir Kushnir Cameron Laird +?ukasz Langa Tino Lange Andrew Langmead Detlef Lannert Modified: python/branches/release31-maint/Misc/NEWS ============================================================================== --- python/branches/release31-maint/Misc/NEWS (original) +++ python/branches/release31-maint/Misc/NEWS Sun Aug 1 23:16:54 2010 @@ -127,6 +127,8 @@ - Issue #1690103: Fix initial namespace for code run with trace.main(). +- Issue #9354: Provide getsockopt() in asyncore's file_wrapper. + - Issue #9448: Fix a leak of OS resources (mutexes or semaphores) when re-initializing a buffered IO object by calling its ``__init__`` method. From python-checkins at python.org Sun Aug 1 23:18:52 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 1 Aug 2010 23:18:52 +0200 (CEST) Subject: [Python-checkins] r83461 - in python/branches/release27-maint: Lib/asyncore.py Lib/test/test_asyncore.py Misc/ACKS Misc/NEWS Message-ID: <20100801211852.72A75EE992@mail.python.org> Author: georg.brandl Date: Sun Aug 1 23:18:52 2010 New Revision: 83461 Log: Merged revisions 83201 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83201 | georg.brandl | 2010-07-28 10:19:35 +0200 (Mi, 28 Jul 2010) | 1 line #9354: Provide getsockopt() in asyncore file_wrapper(). Patch by Lukas Langa. ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Lib/asyncore.py python/branches/release27-maint/Lib/test/test_asyncore.py python/branches/release27-maint/Misc/ACKS python/branches/release27-maint/Misc/NEWS Modified: python/branches/release27-maint/Lib/asyncore.py ============================================================================== --- python/branches/release27-maint/Lib/asyncore.py (original) +++ python/branches/release27-maint/Lib/asyncore.py Sun Aug 1 23:18:52 2010 @@ -607,6 +607,14 @@ def send(self, *args): return os.write(self.fd, *args) + def getsockopt(self, level, optname, buflen=None): + if (level == socket.SOL_SOCKET and + optname == socket.SO_ERROR and + not buflen): + return 0 + raise NotImplementedError("Only asyncore specific behaviour " + "implemented.") + read = recv write = send Modified: python/branches/release27-maint/Lib/test/test_asyncore.py ============================================================================== --- python/branches/release27-maint/Lib/test/test_asyncore.py (original) +++ python/branches/release27-maint/Lib/test/test_asyncore.py Sun Aug 1 23:18:52 2010 @@ -428,6 +428,19 @@ w.close() self.assertEqual(file(TESTFN).read(), self.d + d1 + d2) + @unittest.skipUnless(hasattr(asyncore, 'file_dispatcher'), + 'asyncore.file_dispatcher required') + def test_dispatcher(self): + fd = os.open(TESTFN, os.O_RDONLY) + data = [] + class FileDispatcher(asyncore.file_dispatcher): + def handle_read(self): + data.append(self.recv(29)) + s = FileDispatcher(fd) + os.close(fd) + asyncore.loop(timeout=0.01, use_poll=True, count=2) + self.assertEqual(b"".join(data), self.d) + class BaseTestHandler(asyncore.dispatcher): Modified: python/branches/release27-maint/Misc/ACKS ============================================================================== --- python/branches/release27-maint/Misc/ACKS (original) +++ python/branches/release27-maint/Misc/ACKS Sun Aug 1 23:18:52 2010 @@ -438,8 +438,8 @@ Andrew Kuchling Vladimir Kushnir Cameron Laird -Tino Lange ?ukasz Langa +Tino Lange Andrew Langmead Detlef Lannert Soren Larsen Modified: python/branches/release27-maint/Misc/NEWS ============================================================================== --- python/branches/release27-maint/Misc/NEWS (original) +++ python/branches/release27-maint/Misc/NEWS Sun Aug 1 23:18:52 2010 @@ -24,6 +24,8 @@ Library ------- +- Issue #9354: Provide getsockopt() in asyncore's file_wrapper. + - Issue #4108: In urllib.robotparser, if there are multiple 'User-agent: *' entries, consider the first one. @@ -951,6 +953,7 @@ Library ------- + - Issue #1555570: email no longer inserts extra blank lines when a \r\n combo crosses an 8192 byte boundary. From python-checkins at python.org Sun Aug 1 23:19:50 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 1 Aug 2010 23:19:50 +0200 (CEST) Subject: [Python-checkins] r83462 - python/branches/release27-maint Message-ID: <20100801211950.28DA0EE992@mail.python.org> Author: georg.brandl Date: Sun Aug 1 23:19:50 2010 New Revision: 83462 Log: Blocked revisions 83181 via svnmerge ........ r83181 | georg.brandl | 2010-07-27 20:19:21 +0200 (Di, 27 Jul 2010) | 1 line Update Sphinx to 1.0.1. ........ Modified: python/branches/release27-maint/ (props changed) From python-checkins at python.org Sun Aug 1 23:19:50 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 1 Aug 2010 23:19:50 +0200 (CEST) Subject: [Python-checkins] r83463 - python/branches/release31-maint Message-ID: <20100801211950.DF696EE992@mail.python.org> Author: georg.brandl Date: Sun Aug 1 23:19:50 2010 New Revision: 83463 Log: Blocked revisions 83181 via svnmerge ........ r83181 | georg.brandl | 2010-07-27 20:19:21 +0200 (Di, 27 Jul 2010) | 1 line Update Sphinx to 1.0.1. ........ Modified: python/branches/release31-maint/ (props changed) From python-checkins at python.org Sun Aug 1 23:20:17 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 1 Aug 2010 23:20:17 +0200 (CEST) Subject: [Python-checkins] r83464 - python/branches/release26-maint Message-ID: <20100801212017.9355AEEAEB@mail.python.org> Author: georg.brandl Date: Sun Aug 1 23:20:17 2010 New Revision: 83464 Log: Blocked revisions 83174-83176 via svnmerge ................ r83174 | georg.brandl | 2010-07-27 10:40:57 +0200 (Di, 27 Jul 2010) | 12 lines Blocked revisions 82526,82593 via svnmerge ........ r82526 | georg.brandl | 2010-07-04 09:25:18 +0200 (So, 04 Jul 2010) | 1 line Remove old and unsafe Dbm demo class. ........ r82593 | georg.brandl | 2010-07-05 22:13:06 +0200 (Mo, 05 Jul 2010) | 1 line Remove Dbm. ........ ................ r83175 | georg.brandl | 2010-07-27 10:45:35 +0200 (Di, 27 Jul 2010) | 13 lines Merged revisions 82594,82599 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r82594 | georg.brandl | 2010-07-05 22:13:41 +0200 (Mo, 05 Jul 2010) | 1 line Update Vec class constructor, remove indirection via function, use operator module. ........ r82599 | alexander.belopolsky | 2010-07-05 23:44:05 +0200 (Mo, 05 Jul 2010) | 1 line "Modernized" the demo a little. ........ ................ r83176 | georg.brandl | 2010-07-27 10:46:44 +0200 (Di, 27 Jul 2010) | 8 lines Blocked revisions 82759 via svnmerge ........ r82759 | georg.brandl | 2010-07-10 12:32:36 +0200 (Sa, 10 Jul 2010) | 1 line #3071: tell how many values were expected when unpacking too many. ........ ................ Modified: python/branches/release26-maint/ (props changed) From python-checkins at python.org Sun Aug 1 23:23:10 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 1 Aug 2010 23:23:10 +0200 (CEST) Subject: [Python-checkins] r83465 - in python/branches/release31-maint: Doc/c-api/unicode.rst Doc/documenting/markup.rst Doc/install/index.rst Misc/developers.txt Message-ID: <20100801212310.DDD0EEE992@mail.python.org> Author: georg.brandl Date: Sun Aug 1 23:23:10 2010 New Revision: 83465 Log: Merged revisions 83160-83161,83166,83169,83171 via svnmerge from svn+ssh://svn.python.org/python/branches/py3k ........ r83160 | georg.brandl | 2010-07-26 10:51:42 +0200 (Mo, 26 Jul 2010) | 1 line #9381: fix markup. ........ r83161 | georg.brandl | 2010-07-26 11:33:12 +0200 (Mo, 26 Jul 2010) | 1 line Add Brian Quinlan. ........ r83166 | georg.brandl | 2010-07-26 17:11:49 +0200 (Mo, 26 Jul 2010) | 1 line Fix grammar. ........ r83169 | georg.brandl | 2010-07-26 19:09:32 +0200 (Mo, 26 Jul 2010) | 1 line Add Reid. ........ r83171 | georg.brandl | 2010-07-26 23:12:13 +0200 (Mo, 26 Jul 2010) | 1 line Clarify. ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Doc/c-api/unicode.rst python/branches/release31-maint/Doc/documenting/markup.rst python/branches/release31-maint/Doc/install/index.rst python/branches/release31-maint/Misc/developers.txt Modified: python/branches/release31-maint/Doc/c-api/unicode.rst ============================================================================== --- python/branches/release31-maint/Doc/c-api/unicode.rst (original) +++ python/branches/release31-maint/Doc/c-api/unicode.rst Sun Aug 1 23:23:10 2010 @@ -358,7 +358,7 @@ :cdata:`Py_FileSystemEncoding` should be used as the encoding, and ``"surrogateescape"`` should be used as the error handler (:pep:`383`). To encode file names during argument parsing, the ``"O&"`` converter should be -used, passsing :func:PyUnicode_FSConverter as the conversion function: +used, passsing :func:`PyUnicode_FSConverter` as the conversion function: .. cfunction:: int PyUnicode_FSConverter(PyObject* obj, void* result) Modified: python/branches/release31-maint/Doc/documenting/markup.rst ============================================================================== --- python/branches/release31-maint/Doc/documenting/markup.rst (original) +++ python/branches/release31-maint/Doc/documenting/markup.rst Sun Aug 1 23:23:10 2010 @@ -200,8 +200,8 @@ .. describe:: cmdoption - Describes a command line option or switch. Option argument names should be - enclosed in angle brackets. Example:: + Describes a Python command line option or switch. Option argument names + should be enclosed in angle brackets. Example:: .. cmdoption:: -m Modified: python/branches/release31-maint/Doc/install/index.rst ============================================================================== --- python/branches/release31-maint/Doc/install/index.rst (original) +++ python/branches/release31-maint/Doc/install/index.rst Sun Aug 1 23:23:10 2010 @@ -314,8 +314,8 @@ stash of Python modules. This scheme's name is derived from the idea of a "home" directory on Unix, since it's not unusual for a Unix user to make their home directory have a layout similar to :file:`/usr/` or :file:`/usr/local/`. -This scheme can be used by anyone, regardless of the operating system their -installing for. +This scheme can be used by anyone, regardless of the operating system they +are installing for. Installing a new module distribution is as simple as :: Modified: python/branches/release31-maint/Misc/developers.txt ============================================================================== --- python/branches/release31-maint/Misc/developers.txt (original) +++ python/branches/release31-maint/Misc/developers.txt Sun Aug 1 23:23:10 2010 @@ -20,6 +20,42 @@ Permissions History ------------------- +- Brian Quinlan was given commit access on Jul 26 2010 by GFB, + for work related to PEP 3148. + +- Reid Kleckner was given commit access on Jul 11 2010 by GFB, + for work on the py3k-jit branch, at suggestion of the Unladen + Swallow team. + +- Alexander Belopolsky was given commit access on May 25 2010 + by MvL at suggestion of Mark Dickinson. + +- Tim Golden was given commit access on April 21 2010 by MvL, + at suggestion of Michael Foord. + +- Giampaolo Rodol? was given commit access on April 17 2010 by + MvL, at suggestion of R. David Murray. + +- Jean-Paul Calderone was given commit access on April 6 2010 by + GFB, at suggestion of Michael Foord and others. + +- Brian Curtin was given commit access on March 24 2010 by MvL. + +- Florent Xicluna was given commit access on February 25 2010 by + MvL, based on Antoine Pitrou's recommendation. + +- Dino Viehland was given SVN access on February 23 2010 by Brett + Cannon, for backporting tests from IronPython. + +- Larry Hastings was given SVN access on February 22 2010 by + Andrew Kuchling, based on Brett Cannon's recommendation. + +- Victor Stinner was given SVN access on January 30 2010 by MvL, + at recommendation by Mark Dickinson and Amaury Forgeot d'Arc. + +- Stefan Krah was given SVN access on January 5 2010 by GFB, at + suggestion of Mark Dickinson, for work on the decimal module. + - Doug Hellmann was given SVN access on September 19 2009 by GFB, at suggestion of Jesse Noller, for documentation work. From python-checkins at python.org Sun Aug 1 23:23:50 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 1 Aug 2010 23:23:50 +0200 (CEST) Subject: [Python-checkins] r83466 - in python/branches/release27-maint: Doc/documenting/markup.rst Doc/install/index.rst Doc/library/argparse.rst Misc/developers.txt Message-ID: <20100801212350.D4A0EEE992@mail.python.org> Author: georg.brandl Date: Sun Aug 1 23:23:50 2010 New Revision: 83466 Log: Merged revisions 83160-83161,83166,83168-83169,83171 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83160 | georg.brandl | 2010-07-26 10:51:42 +0200 (Mo, 26 Jul 2010) | 1 line #9381: fix markup. ........ r83161 | georg.brandl | 2010-07-26 11:33:12 +0200 (Mo, 26 Jul 2010) | 1 line Add Brian Quinlan. ........ r83166 | georg.brandl | 2010-07-26 17:11:49 +0200 (Mo, 26 Jul 2010) | 1 line Fix grammar. ........ r83168 | georg.brandl | 2010-07-26 19:00:20 +0200 (Mo, 26 Jul 2010) | 1 line Fix indentation in example. ........ r83169 | georg.brandl | 2010-07-26 19:09:32 +0200 (Mo, 26 Jul 2010) | 1 line Add Reid. ........ r83171 | georg.brandl | 2010-07-26 23:12:13 +0200 (Mo, 26 Jul 2010) | 1 line Clarify. ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Doc/documenting/markup.rst python/branches/release27-maint/Doc/install/index.rst python/branches/release27-maint/Doc/library/argparse.rst python/branches/release27-maint/Misc/developers.txt Modified: python/branches/release27-maint/Doc/documenting/markup.rst ============================================================================== --- python/branches/release27-maint/Doc/documenting/markup.rst (original) +++ python/branches/release27-maint/Doc/documenting/markup.rst Sun Aug 1 23:23:50 2010 @@ -200,8 +200,8 @@ .. describe:: cmdoption - Describes a command line option or switch. Option argument names should be - enclosed in angle brackets. Example:: + Describes a Python command line option or switch. Option argument names + should be enclosed in angle brackets. Example:: .. cmdoption:: -m Modified: python/branches/release27-maint/Doc/install/index.rst ============================================================================== --- python/branches/release27-maint/Doc/install/index.rst (original) +++ python/branches/release27-maint/Doc/install/index.rst Sun Aug 1 23:23:50 2010 @@ -314,8 +314,8 @@ stash of Python modules. This scheme's name is derived from the idea of a "home" directory on Unix, since it's not unusual for a Unix user to make their home directory have a layout similar to :file:`/usr/` or :file:`/usr/local/`. -This scheme can be used by anyone, regardless of the operating system their -installing for. +This scheme can be used by anyone, regardless of the operating system they +are installing for. Installing a new module distribution is as simple as :: Modified: python/branches/release27-maint/Doc/library/argparse.rst ============================================================================== --- python/branches/release27-maint/Doc/library/argparse.rst (original) +++ python/branches/release27-maint/Doc/library/argparse.rst Sun Aug 1 23:23:50 2010 @@ -698,8 +698,8 @@ >>> class FooAction(argparse.Action): ... def __call__(self, parser, namespace, values, option_string=None): - ... print '%r %r %r' % (namespace, values, option_string) - ... setattr(namespace, self.dest, values) + ... print '%r %r %r' % (namespace, values, option_string) + ... setattr(namespace, self.dest, values) ... >>> parser = argparse.ArgumentParser() >>> parser.add_argument('--foo', action=FooAction) Modified: python/branches/release27-maint/Misc/developers.txt ============================================================================== --- python/branches/release27-maint/Misc/developers.txt (original) +++ python/branches/release27-maint/Misc/developers.txt Sun Aug 1 23:23:50 2010 @@ -20,6 +20,13 @@ Permissions History ------------------- +- Brian Quinlan was given commit access on Jul 26 2010 by GFB, + for work related to PEP 3148. + +- Reid Kleckner was given commit access on Jul 11 2010 by GFB, + for work on the py3k-jit branch, at suggestion of the Unladen + Swallow team. + - Alexander Belopolsky was given commit access on May 25 2010 by MvL at suggestion of Mark Dickinson. From python-checkins at python.org Sun Aug 1 23:24:23 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 1 Aug 2010 23:24:23 +0200 (CEST) Subject: [Python-checkins] r83467 - python/branches/release31-maint Message-ID: <20100801212423.D6B32EE992@mail.python.org> Author: georg.brandl Date: Sun Aug 1 23:24:23 2010 New Revision: 83467 Log: Blocked revisions 83168 via svnmerge ........ r83168 | georg.brandl | 2010-07-26 19:00:20 +0200 (Mo, 26 Jul 2010) | 1 line Fix indentation in example. ........ Modified: python/branches/release31-maint/ (props changed) From python-checkins at python.org Sun Aug 1 23:24:48 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 1 Aug 2010 23:24:48 +0200 (CEST) Subject: [Python-checkins] r83468 - python/branches/release31-maint Message-ID: <20100801212448.5C9BFEE992@mail.python.org> Author: georg.brandl Date: Sun Aug 1 23:24:48 2010 New Revision: 83468 Log: Blocked revisions 83107 via svnmerge ........ r83107 | georg.brandl | 2010-07-23 18:55:42 +0200 (Fr, 23 Jul 2010) | 1 line Update to 1.0. ........ Modified: python/branches/release31-maint/ (props changed) From python-checkins at python.org Sun Aug 1 23:24:49 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 1 Aug 2010 23:24:49 +0200 (CEST) Subject: [Python-checkins] r83469 - python/branches/release27-maint Message-ID: <20100801212449.5EAA3EE9B8@mail.python.org> Author: georg.brandl Date: Sun Aug 1 23:24:49 2010 New Revision: 83469 Log: Blocked revisions 83107 via svnmerge ........ r83107 | georg.brandl | 2010-07-23 18:55:42 +0200 (Fr, 23 Jul 2010) | 1 line Update to 1.0. ........ Modified: python/branches/release27-maint/ (props changed) From python-checkins at python.org Sun Aug 1 23:25:46 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 1 Aug 2010 23:25:46 +0200 (CEST) Subject: [Python-checkins] r83470 - in python/branches/release31-maint: Doc/library/urllib.request.rst Doc/whatsnew/2.0.rst Doc/whatsnew/2.5.rst Message-ID: <20100801212546.DA06AEE992@mail.python.org> Author: georg.brandl Date: Sun Aug 1 23:25:46 2010 New Revision: 83470 Log: Merged revisions 83106 via svnmerge from svn+ssh://svn.python.org/python/branches/py3k ........ r83106 | georg.brandl | 2010-07-23 18:55:26 +0200 (Fr, 23 Jul 2010) | 1 line Fix some markup glitches. ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Doc/library/urllib.request.rst python/branches/release31-maint/Doc/whatsnew/2.0.rst python/branches/release31-maint/Doc/whatsnew/2.5.rst Modified: python/branches/release31-maint/Doc/library/urllib.request.rst ============================================================================== --- python/branches/release31-maint/Doc/library/urllib.request.rst (original) +++ python/branches/release31-maint/Doc/library/urllib.request.rst Sun Aug 1 23:25:46 2010 @@ -638,7 +638,8 @@ :meth:`unknown_open`. Note that the implementation of these methods may involve calls of the parent - :class:`OpenerDirector` instance's :meth:`.open` and :meth:`.error` methods. + :class:`OpenerDirector` instance's :meth:`~OpenerDirector.open` and + :meth:`~OpenerDirector.error` methods. #. Every handler with a method named like :meth:`protocol_response` has that method called to post-process the response. Modified: python/branches/release31-maint/Doc/whatsnew/2.0.rst ============================================================================== --- python/branches/release31-maint/Doc/whatsnew/2.0.rst (original) +++ python/branches/release31-maint/Doc/whatsnew/2.0.rst Sun Aug 1 23:25:46 2010 @@ -656,7 +656,7 @@ The change which will probably break the most code is tightening up the arguments accepted by some methods. Some methods would take multiple arguments and treat them as a tuple, particularly various list methods such as -:meth:`.append` and :meth:`.insert`. In earlier versions of Python, if ``L`` is +:meth:`append` and :meth:`insert`. In earlier versions of Python, if ``L`` is a list, ``L.append( 1,2 )`` appends the tuple ``(1,2)`` to the list. In Python 2.0 this causes a :exc:`TypeError` exception to be raised, with the message: 'append requires exactly 1 argument; 2 given'. The fix is to simply add an Modified: python/branches/release31-maint/Doc/whatsnew/2.5.rst ============================================================================== --- python/branches/release31-maint/Doc/whatsnew/2.5.rst (original) +++ python/branches/release31-maint/Doc/whatsnew/2.5.rst Sun Aug 1 23:25:46 2010 @@ -1765,7 +1765,7 @@ http://effbot.org/zone/element-index.htm. ElementTree represents an XML document as a tree of element nodes. The text -content of the document is stored as the :attr:`.text` and :attr:`.tail` +content of the document is stored as the :attr:`text` and :attr:`tail` attributes of (This is one of the major differences between ElementTree and the Document Object Model; in the DOM there are many different types of node, including :class:`TextNode`.) From python-checkins at python.org Sun Aug 1 23:26:45 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 1 Aug 2010 23:26:45 +0200 (CEST) Subject: [Python-checkins] r83471 - in python/branches/release27-maint: Doc/library/urllib2.rst Doc/whatsnew/2.0.rst Doc/whatsnew/2.5.rst Message-ID: <20100801212645.AA62CEE992@mail.python.org> Author: georg.brandl Date: Sun Aug 1 23:26:45 2010 New Revision: 83471 Log: Merged revisions 83106 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83106 | georg.brandl | 2010-07-23 18:55:26 +0200 (Fr, 23 Jul 2010) | 1 line Fix some markup glitches. ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Doc/library/urllib2.rst python/branches/release27-maint/Doc/whatsnew/2.0.rst python/branches/release27-maint/Doc/whatsnew/2.5.rst Modified: python/branches/release27-maint/Doc/library/urllib2.rst ============================================================================== --- python/branches/release27-maint/Doc/library/urllib2.rst (original) +++ python/branches/release27-maint/Doc/library/urllib2.rst Sun Aug 1 23:26:45 2010 @@ -464,7 +464,8 @@ named :meth:`unknown_open`. Note that the implementation of these methods may involve calls of the parent - :class:`OpenerDirector` instance's :meth:`.open` and :meth:`.error` methods. + :class:`OpenerDirector` instance's :meth:`~OpenerDirector.open` and + :meth:`~OpenerDirector.error` methods. #. Every handler with a method named like :samp:`{protocol}_response` has that method called to post-process the response. Modified: python/branches/release27-maint/Doc/whatsnew/2.0.rst ============================================================================== --- python/branches/release27-maint/Doc/whatsnew/2.0.rst (original) +++ python/branches/release27-maint/Doc/whatsnew/2.0.rst Sun Aug 1 23:26:45 2010 @@ -656,7 +656,7 @@ The change which will probably break the most code is tightening up the arguments accepted by some methods. Some methods would take multiple arguments and treat them as a tuple, particularly various list methods such as -:meth:`.append` and :meth:`.insert`. In earlier versions of Python, if ``L`` is +:meth:`append` and :meth:`insert`. In earlier versions of Python, if ``L`` is a list, ``L.append( 1,2 )`` appends the tuple ``(1,2)`` to the list. In Python 2.0 this causes a :exc:`TypeError` exception to be raised, with the message: 'append requires exactly 1 argument; 2 given'. The fix is to simply add an Modified: python/branches/release27-maint/Doc/whatsnew/2.5.rst ============================================================================== --- python/branches/release27-maint/Doc/whatsnew/2.5.rst (original) +++ python/branches/release27-maint/Doc/whatsnew/2.5.rst Sun Aug 1 23:26:45 2010 @@ -1765,7 +1765,7 @@ http://effbot.org/zone/element-index.htm. ElementTree represents an XML document as a tree of element nodes. The text -content of the document is stored as the :attr:`.text` and :attr:`.tail` +content of the document is stored as the :attr:`text` and :attr:`tail` attributes of (This is one of the major differences between ElementTree and the Document Object Model; in the DOM there are many different types of node, including :class:`TextNode`.) From python-checkins at python.org Sun Aug 1 23:27:48 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 1 Aug 2010 23:27:48 +0200 (CEST) Subject: [Python-checkins] r83472 - in python/branches/release31-maint: Lib/ntpath.py Message-ID: <20100801212748.D907CEE987@mail.python.org> Author: georg.brandl Date: Sun Aug 1 23:27:48 2010 New Revision: 83472 Log: Merged revisions 83065 via svnmerge from svn+ssh://svn.python.org/python/branches/py3k ........ r83065 | georg.brandl | 2010-07-23 10:46:35 +0200 (Fr, 23 Jul 2010) | 1 line Use augassign. ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Lib/ntpath.py Modified: python/branches/release31-maint/Lib/ntpath.py ============================================================================== --- python/branches/release31-maint/Lib/ntpath.py (original) +++ python/branches/release31-maint/Lib/ntpath.py Sun Aug 1 23:27:48 2010 @@ -277,7 +277,7 @@ # set i to index beyond p's last slash i = len(p) while i and p[i-1] not in seps: - i = i - 1 + i -= 1 head, tail = p[:i], p[i:] # now tail has no slashes # remove trailing slashes from head, unless it's all slashes head2 = head @@ -356,7 +356,7 @@ return path i, n = 1, len(path) while i < n and path[i] not in _get_bothseps(path): - i = i + 1 + i += 1 if 'HOME' in os.environ: userhome = os.environ['HOME'] @@ -425,21 +425,21 @@ pathlen = len(path) try: index = path.index(c) - res = res + c + path[:index + 1] + res += c + path[:index + 1] except ValueError: - res = res + path + res += path index = pathlen - 1 elif c == percent: # variable or '%' if path[index + 1:index + 2] == percent: - res = res + c - index = index + 1 + res += c + index += 1 else: path = path[index+1:] pathlen = len(path) try: index = path.index(percent) except ValueError: - res = res + percent + path + res += percent + path index = pathlen - 1 else: var = path[:index] @@ -451,11 +451,11 @@ value = '%' + var + '%' if isinstance(path, bytes): value = value.encode('ascii') - res = res + value + res += value elif c == dollar: # variable or '$$' if path[index + 1:index + 2] == dollar: - res = res + c - index = index + 1 + res += c + index += 1 elif path[index + 1:index + 2] == brace: path = path[index+2:] pathlen = len(path) @@ -473,23 +473,23 @@ value = '${' + var + '}' if isinstance(path, bytes): value = value.encode('ascii') - res = res + value + res += value except ValueError: if isinstance(path, bytes): - res = res + b'${' + path + res += b'${' + path else: - res = res + '${' + path + res += '${' + path index = pathlen - 1 else: var = '' - index = index + 1 + index += 1 c = path[index:index + 1] while c and c in varchars: if isinstance(path, bytes): - var = var + c.decode('ascii') + var += c.decode('ascii') else: - var = var + c - index = index + 1 + var += c + index += 1 c = path[index:index + 1] if var in os.environ: value = os.environ[var] @@ -497,12 +497,12 @@ value = '$' + var if isinstance(path, bytes): value = value.encode('ascii') - res = res + value + res += value if c: - index = index - 1 + index -= 1 else: - res = res + c - index = index + 1 + res += c + index += 1 return res @@ -526,7 +526,7 @@ # collapse initial backslashes if path.startswith(sep): - prefix = prefix + sep + prefix += sep path = path.lstrip(sep) comps = path.split(sep) From python-checkins at python.org Sun Aug 1 23:27:58 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 1 Aug 2010 23:27:58 +0200 (CEST) Subject: [Python-checkins] r83473 - python/branches/release27-maint Message-ID: <20100801212758.35324EE9A5@mail.python.org> Author: georg.brandl Date: Sun Aug 1 23:27:58 2010 New Revision: 83473 Log: Blocked revisions 83065 via svnmerge ........ r83065 | georg.brandl | 2010-07-23 10:46:35 +0200 (Fr, 23 Jul 2010) | 1 line Use augassign. ........ Modified: python/branches/release27-maint/ (props changed) From python-checkins at python.org Sun Aug 1 23:28:39 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 1 Aug 2010 23:28:39 +0200 (CEST) Subject: [Python-checkins] r83474 - in python/branches/release31-maint: Doc/tutorial/classes.rst Message-ID: <20100801212839.D44CFEE987@mail.python.org> Author: georg.brandl Date: Sun Aug 1 23:28:39 2010 New Revision: 83474 Log: Merged revisions 82965 via svnmerge from svn+ssh://svn.python.org/python/branches/py3k ........ r82965 | georg.brandl | 2010-07-19 13:28:05 +0200 (Mo, 19 Jul 2010) | 1 line Clarification. Yay importlib! ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Doc/tutorial/classes.rst Modified: python/branches/release31-maint/Doc/tutorial/classes.rst ============================================================================== --- python/branches/release31-maint/Doc/tutorial/classes.rst (original) +++ python/branches/release31-maint/Doc/tutorial/classes.rst Sun Aug 1 23:28:39 2010 @@ -737,7 +737,7 @@ StopIteration Having seen the mechanics behind the iterator protocol, it is easy to add -iterator behavior to your classes. Define a :meth:`__iter__` method which +iterator behavior to your classes. Define an :meth:`__iter__` method which returns an object with a :meth:`__next__` method. If the class defines :meth:`__next__`, then :meth:`__iter__` can just return ``self``:: @@ -754,7 +754,10 @@ self.index = self.index - 1 return self.data[self.index] - >>> for char in Reverse('spam'): + >>> rev = Reverse('spam') + >>> iter(rev) + <__main__.Reverse object at 0x00A1DB50> + >>> for char in rev: ... print(char) ... m From python-checkins at python.org Sun Aug 1 23:28:48 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 1 Aug 2010 23:28:48 +0200 (CEST) Subject: [Python-checkins] r83475 - in python/branches/release27-maint: Doc/tutorial/classes.rst Message-ID: <20100801212848.10060EEA7B@mail.python.org> Author: georg.brandl Date: Sun Aug 1 23:28:47 2010 New Revision: 83475 Log: Merged revisions 82965 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r82965 | georg.brandl | 2010-07-19 13:28:05 +0200 (Mo, 19 Jul 2010) | 1 line Clarification. Yay importlib! ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Doc/tutorial/classes.rst Modified: python/branches/release27-maint/Doc/tutorial/classes.rst ============================================================================== --- python/branches/release27-maint/Doc/tutorial/classes.rst (original) +++ python/branches/release27-maint/Doc/tutorial/classes.rst Sun Aug 1 23:28:47 2010 @@ -693,7 +693,7 @@ StopIteration Having seen the mechanics behind the iterator protocol, it is easy to add -iterator behavior to your classes. Define a :meth:`__iter__` method which +iterator behavior to your classes. Define an :meth:`__iter__` method which returns an object with a :meth:`next` method. If the class defines :meth:`next`, then :meth:`__iter__` can just return ``self``:: @@ -710,7 +710,10 @@ self.index = self.index - 1 return self.data[self.index] - >>> for char in Reverse('spam'): + >>> rev = Reverse('spam') + >>> iter(rev) + <__main__.Reverse object at 0x00A1DB50> + >>> for char in rev: ... print char ... m From python-checkins at python.org Sun Aug 1 23:29:32 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 1 Aug 2010 23:29:32 +0200 (CEST) Subject: [Python-checkins] r83476 - python/branches/release31-maint Message-ID: <20100801212932.998F2EE987@mail.python.org> Author: georg.brandl Date: Sun Aug 1 23:29:32 2010 New Revision: 83476 Log: Blocked revisions 82949,82964 via svnmerge ........ r82949 | georg.brandl | 2010-07-18 12:11:03 +0200 (So, 18 Jul 2010) | 1 line #9279: remove the pdb.doc file, put its contents in pdb.__doc__. Also sync this and the pdb docs, introduce a new directive for pdb commands and a role to link to them. ........ r82964 | georg.brandl | 2010-07-19 10:02:46 +0200 (Mo, 19 Jul 2010) | 1 line pydoc.pager does not promise to use $PAGER. ........ Modified: python/branches/release31-maint/ (props changed) From python-checkins at python.org Sun Aug 1 23:29:37 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 1 Aug 2010 23:29:37 +0200 (CEST) Subject: [Python-checkins] r83477 - python/branches/release27-maint Message-ID: <20100801212937.5F641EEA09@mail.python.org> Author: georg.brandl Date: Sun Aug 1 23:29:37 2010 New Revision: 83477 Log: Blocked revisions 82949,82964 via svnmerge ........ r82949 | georg.brandl | 2010-07-18 12:11:03 +0200 (So, 18 Jul 2010) | 1 line #9279: remove the pdb.doc file, put its contents in pdb.__doc__. Also sync this and the pdb docs, introduce a new directive for pdb commands and a role to link to them. ........ r82964 | georg.brandl | 2010-07-19 10:02:46 +0200 (Mo, 19 Jul 2010) | 1 line pydoc.pager does not promise to use $PAGER. ........ Modified: python/branches/release27-maint/ (props changed) From python-checkins at python.org Sun Aug 1 23:32:08 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 1 Aug 2010 23:32:08 +0200 (CEST) Subject: [Python-checkins] r83478 - in python/branches/release31-maint: Doc/documenting/markup.rst Doc/library/io.rst Doc/library/select.rst Message-ID: <20100801213208.55F8FEE983@mail.python.org> Author: georg.brandl Date: Sun Aug 1 23:32:08 2010 New Revision: 83478 Log: Merged revisions 82871,82960-82961 via svnmerge from svn+ssh://svn.python.org/python/branches/py3k ........ r82871 | georg.brandl | 2010-07-14 10:00:22 +0200 (Mi, 14 Jul 2010) | 1 line #9258: fix copy-paste errors. ........ r82960 | georg.brandl | 2010-07-19 08:52:35 +0200 (Mo, 19 Jul 2010) | 1 line Clarify. ........ r82961 | georg.brandl | 2010-07-19 08:57:52 +0200 (Mo, 19 Jul 2010) | 1 line Clarify :option: description. ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Doc/documenting/markup.rst python/branches/release31-maint/Doc/library/io.rst python/branches/release31-maint/Doc/library/select.rst Modified: python/branches/release31-maint/Doc/documenting/markup.rst ============================================================================== --- python/branches/release31-maint/Doc/documenting/markup.rst (original) +++ python/branches/release31-maint/Doc/documenting/markup.rst Sun Aug 1 23:32:08 2010 @@ -502,8 +502,9 @@ .. describe:: option - A command-line option to an executable program. The leading hyphen(s) must - be included. + A command-line option of Python. The leading hyphen(s) must be included. + If a matching ``cmdoption`` directive exists, it is linked to. For options + of other programs or scripts, use simple ````code```` markup. .. describe:: program Modified: python/branches/release31-maint/Doc/library/io.rst ============================================================================== --- python/branches/release31-maint/Doc/library/io.rst (original) +++ python/branches/release31-maint/Doc/library/io.rst Sun Aug 1 23:32:08 2010 @@ -56,8 +56,8 @@ Open *file* and return a corresponding stream. If the file cannot be opened, an :exc:`IOError` is raised. - *file* is either a string or bytes object giving the name (and the path if - the file isn't in the current working directory) of the file to be opened or + *file* is either a string or bytes object giving the pathname (absolute or + relative to the current working directory) of the file to be opened or an integer file descriptor of the file to be wrapped. (If a file descriptor is given, it is closed when the returned I/O object is closed, unless *closefd* is set to ``False``.) Modified: python/branches/release31-maint/Doc/library/select.rst ============================================================================== --- python/branches/release31-maint/Doc/library/select.rst (original) +++ python/branches/release31-maint/Doc/library/select.rst Sun Aug 1 23:32:08 2010 @@ -42,14 +42,14 @@ .. function:: kqueue() - (Only supported on BSD.) Returns a kernel queue object object; see section + (Only supported on BSD.) Returns a kernel queue object; see section :ref:`kqueue-objects` below for the methods supported by kqueue objects. .. function:: kevent(ident, filter=KQ_FILTER_READ, flags=KQ_EV_ADD, fflags=0, data=0, udata=0) - (Only supported on BSD.) Returns a kernel event object object; see section - :ref:`kevent-objects` below for the methods supported by kqueue objects. + (Only supported on BSD.) Returns a kernel event object; see section + :ref:`kevent-objects` below for the methods supported by kevent objects. .. function:: select(rlist, wlist, xlist[, timeout]) From python-checkins at python.org Sun Aug 1 23:33:01 2010 From: python-checkins at python.org (mark.dickinson) Date: Sun, 1 Aug 2010 23:33:01 +0200 (CEST) Subject: [Python-checkins] r83479 - python/branches/py3k/Makefile.pre.in Message-ID: <20100801213301.B8B7DEE983@mail.python.org> Author: mark.dickinson Date: Sun Aug 1 23:33:01 2010 New Revision: 83479 Log: Don't delete Lib/test/data/README when doing 'make distclean' Modified: python/branches/py3k/Makefile.pre.in Modified: python/branches/py3k/Makefile.pre.in ============================================================================== --- python/branches/py3k/Makefile.pre.in (original) +++ python/branches/py3k/Makefile.pre.in Sun Aug 1 23:33:01 2010 @@ -1204,7 +1204,9 @@ # remove all generated files, even Makefile[.pre] # Keep configure and Python-ast.[ch], it's possible they can't be generated distclean: clobber - -rm -f Lib/test/data/* + for file in Lib/test/data/* ; do \ + if test "$$file" != "Lib/test/data/README"; then rm "$$file"; fi; \ + done -rm -f core Makefile Makefile.pre config.status \ Modules/Setup Modules/Setup.local Modules/Setup.config \ Misc/python.pc From python-checkins at python.org Sun Aug 1 23:33:42 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 1 Aug 2010 23:33:42 +0200 (CEST) Subject: [Python-checkins] r83480 - in python/branches/release27-maint: Doc/documenting/markup.rst Doc/library/io.rst Doc/library/select.rst Message-ID: <20100801213342.CDA30EE983@mail.python.org> Author: georg.brandl Date: Sun Aug 1 23:33:42 2010 New Revision: 83480 Log: Merged revisions 82871,82960-82961 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r82871 | georg.brandl | 2010-07-14 10:00:22 +0200 (Mi, 14 Jul 2010) | 1 line #9258: fix copy-paste errors. ........ r82960 | georg.brandl | 2010-07-19 08:52:35 +0200 (Mo, 19 Jul 2010) | 1 line Clarify. ........ r82961 | georg.brandl | 2010-07-19 08:57:52 +0200 (Mo, 19 Jul 2010) | 1 line Clarify :option: description. ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Doc/documenting/markup.rst python/branches/release27-maint/Doc/library/io.rst python/branches/release27-maint/Doc/library/select.rst Modified: python/branches/release27-maint/Doc/documenting/markup.rst ============================================================================== --- python/branches/release27-maint/Doc/documenting/markup.rst (original) +++ python/branches/release27-maint/Doc/documenting/markup.rst Sun Aug 1 23:33:42 2010 @@ -502,8 +502,9 @@ .. describe:: option - A command-line option to an executable program. The leading hyphen(s) must - be included. + A command-line option of Python. The leading hyphen(s) must be included. + If a matching ``cmdoption`` directive exists, it is linked to. For options + of other programs or scripts, use simple ````code```` markup. .. describe:: program Modified: python/branches/release27-maint/Doc/library/io.rst ============================================================================== --- python/branches/release27-maint/Doc/library/io.rst (original) +++ python/branches/release27-maint/Doc/library/io.rst Sun Aug 1 23:33:42 2010 @@ -66,11 +66,11 @@ Open *file* and return a corresponding stream. If the file cannot be opened, an :exc:`IOError` is raised. - *file* is either a string giving the name (and the path if the file isn't - in the current working directory) of the file to be opened or an integer - file descriptor of the file to be wrapped. (If a file descriptor is given, - for example, from :func:`os.fdopen`, it is closed when the returned I/O - object is closed, unless *closefd* is set to ``False``.) + *file* is either a string giving the pathname (absolute or + relative to the current working directory) of the file to be opened or + an integer file descriptor of the file to be wrapped. (If a file descriptor + is given, it is closed when the returned I/O object is closed, unless + *closefd* is set to ``False``.) *mode* is an optional string that specifies the mode in which the file is opened. It defaults to ``'r'`` which means open for reading in text mode. Modified: python/branches/release27-maint/Doc/library/select.rst ============================================================================== --- python/branches/release27-maint/Doc/library/select.rst (original) +++ python/branches/release27-maint/Doc/library/select.rst Sun Aug 1 23:33:42 2010 @@ -44,7 +44,7 @@ .. function:: kqueue() - (Only supported on BSD.) Returns a kernel queue object object; see section + (Only supported on BSD.) Returns a kernel queue object; see section :ref:`kqueue-objects` below for the methods supported by kqueue objects. .. versionadded:: 2.6 @@ -52,8 +52,8 @@ .. function:: kevent(ident, filter=KQ_FILTER_READ, flags=KQ_EV_ADD, fflags=0, data=0, udata=0) - (Only supported on BSD.) Returns a kernel event object object; see section - :ref:`kevent-objects` below for the methods supported by kqueue objects. + (Only supported on BSD.) Returns a kernel event object; see section + :ref:`kevent-objects` below for the methods supported by kevent objects. .. versionadded:: 2.6 From python-checkins at python.org Sun Aug 1 23:33:45 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 1 Aug 2010 23:33:45 +0200 (CEST) Subject: [Python-checkins] r83481 - python/branches/release31-maint Message-ID: <20100801213345.73338EE9D9@mail.python.org> Author: georg.brandl Date: Sun Aug 1 23:33:45 2010 New Revision: 83481 Log: Blocked revisions 82848,82872-82874,82951 via svnmerge ........ r82848 | georg.brandl | 2010-07-13 08:38:10 +0200 (Di, 13 Jul 2010) | 1 line Add bytes in literal_eval doc. ........ r82872 | georg.brandl | 2010-07-14 10:53:18 +0200 (Mi, 14 Jul 2010) | 1 line Remove XXX from text. ........ r82873 | georg.brandl | 2010-07-14 10:53:36 +0200 (Mi, 14 Jul 2010) | 1 line Remove unused code that would raise a NameError. ........ r82874 | georg.brandl | 2010-07-14 10:54:40 +0200 (Mi, 14 Jul 2010) | 1 line #9235: fix missing import of sys. ........ r82951 | georg.brandl | 2010-07-18 15:43:32 +0200 (So, 18 Jul 2010) | 1 line #9110: update to ContextDecorator doc. ........ Modified: python/branches/release31-maint/ (props changed) From python-checkins at python.org Sun Aug 1 23:34:07 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 1 Aug 2010 23:34:07 +0200 (CEST) Subject: [Python-checkins] r83482 - python/branches/release27-maint Message-ID: <20100801213407.DD921EEAAF@mail.python.org> Author: georg.brandl Date: Sun Aug 1 23:34:07 2010 New Revision: 83482 Log: Blocked revisions 82848,82951 via svnmerge ........ r82848 | georg.brandl | 2010-07-13 08:38:10 +0200 (Di, 13 Jul 2010) | 1 line Add bytes in literal_eval doc. ........ r82951 | georg.brandl | 2010-07-18 15:43:32 +0200 (So, 18 Jul 2010) | 1 line #9110: update to ContextDecorator doc. ........ Modified: python/branches/release27-maint/ (props changed) From python-checkins at python.org Sun Aug 1 23:36:26 2010 From: python-checkins at python.org (mark.dickinson) Date: Sun, 1 Aug 2010 23:36:26 +0200 (CEST) Subject: [Python-checkins] r83483 - in python/branches/release27-maint: Makefile.pre.in Message-ID: <20100801213626.61EAFEEAF8@mail.python.org> Author: mark.dickinson Date: Sun Aug 1 23:36:26 2010 New Revision: 83483 Log: Merged revisions 83479 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83479 | mark.dickinson | 2010-08-01 22:33:01 +0100 (Sun, 01 Aug 2010) | 1 line Don't delete Lib/test/data/README when doing 'make distclean' ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Makefile.pre.in Modified: python/branches/release27-maint/Makefile.pre.in ============================================================================== --- python/branches/release27-maint/Makefile.pre.in (original) +++ python/branches/release27-maint/Makefile.pre.in Sun Aug 1 23:36:26 2010 @@ -1192,7 +1192,9 @@ # remove all generated files, even Makefile[.pre] # Keep configure and Python-ast.[ch], it's possible they can't be generated distclean: clobber - -rm -f Lib/test/data/* + for file in Lib/test/data/* ; do \ + if test "$$file" != "Lib/test/data/README"; then rm "$$file"; fi; \ + done -rm -f core Makefile Makefile.pre config.status \ Modules/Setup Modules/Setup.local Modules/Setup.config \ Misc/python.pc From python-checkins at python.org Sun Aug 1 23:40:25 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 1 Aug 2010 23:40:25 +0200 (CEST) Subject: [Python-checkins] r83484 - in python/branches/release31-maint: Doc/faq/extending.rst Doc/library/re.rst Message-ID: <20100801214025.F35BDEE987@mail.python.org> Author: georg.brandl Date: Sun Aug 1 23:40:25 2010 New Revision: 83484 Log: Merged revisions 82832,82834 via svnmerge from svn+ssh://svn.python.org/python/branches/py3k ........ r82832 | georg.brandl | 2010-07-12 11:00:29 +0200 (Mo, 12 Jul 2010) | 1 line Take care of duplicate target name warnings. ........ r82834 | georg.brandl | 2010-07-12 11:06:13 +0200 (Mo, 12 Jul 2010) | 1 line Use raw string literals for regexes containing backlash. ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Doc/faq/extending.rst python/branches/release31-maint/Doc/library/re.rst Modified: python/branches/release31-maint/Doc/faq/extending.rst ============================================================================== --- python/branches/release31-maint/Doc/faq/extending.rst (original) +++ python/branches/release31-maint/Doc/faq/extending.rst Sun Aug 1 23:40:25 2010 @@ -26,6 +26,8 @@ C++ objects with constructors are probably not a good idea. +.. _c-wrapper-software: + Writing C is hard; are there any alternatives? ---------------------------------------------- @@ -197,11 +199,7 @@ whole lot of difference between C and C++ -- so the strategy of building a new Python type around a C structure (pointer) type will also work for C++ objects. -For C++ libraries, you can look at `SIP -`_, `CXX -`_, `Boost -`_, `Weave -`_ or `SWIG `_ +For C++ libraries, see :ref:`c-wrapper-software`. I added a module using the Setup file and the make fails; why? Modified: python/branches/release31-maint/Doc/library/re.rst ============================================================================== --- python/branches/release31-maint/Doc/library/re.rst (original) +++ python/branches/release31-maint/Doc/library/re.rst Sun Aug 1 23:40:25 2010 @@ -1226,9 +1226,9 @@ ... random.shuffle(inner_word) ... return m.group(1) + "".join(inner_word) + m.group(3) >>> text = "Professor Abdolmalek, please report your absences promptly." - >>> re.sub("(\w)(\w+)(\w)", repl, text) + >>> re.sub(r"(\w)(\w+)(\w)", repl, text) 'Poefsrosr Aealmlobdk, pslaee reorpt your abnseces plmrptoy.' - >>> re.sub("(\w)(\w+)(\w)", repl, text) + >>> re.sub(r"(\w)(\w+)(\w)", repl, text) 'Pofsroser Aodlambelk, plasee reoprt yuor asnebces potlmrpy.' From python-checkins at python.org Sun Aug 1 23:44:27 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 1 Aug 2010 23:44:27 +0200 (CEST) Subject: [Python-checkins] r83485 - python/branches/release26-maint Message-ID: <20100801214427.D097BEEA09@mail.python.org> Author: georg.brandl Date: Sun Aug 1 23:44:27 2010 New Revision: 83485 Log: Blocked revisions 83267,83282,83418-83419,83434,83439,83446,83450,83459,83462,83469,83473,83477,83482-83483 via svnmerge ................ r83267 | georg.brandl | 2010-07-30 11:18:49 +0200 (Fr, 30 Jul 2010) | 1 line #7539: use _saferepr() for printing exceptions from pdb. ................ r83282 | georg.brandl | 2010-07-30 16:14:42 +0200 (Fr, 30 Jul 2010) | 1 line Revert r83267, as it breaks a few doctests and generally leads to ugly truncated output. ................ r83418 | georg.brandl | 2010-08-01 20:41:59 +0200 (So, 01 Aug 2010) | 9 lines Merged revisions 83395 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83395 | georg.brandl | 2010-08-01 10:49:18 +0200 (So, 01 Aug 2010) | 1 line #8821: do not rely on Unicode strings being terminated with a \u0000, rather explicitly check range before looking for a second surrogate character. ........ ................ r83419 | georg.brandl | 2010-08-01 20:43:29 +0200 (So, 01 Aug 2010) | 9 lines Merged revisions 83417 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83417 | georg.brandl | 2010-08-01 20:38:26 +0200 (So, 01 Aug 2010) | 1 line #5776: fix mistakes in python specfile. (Nobody probably uses it anyway.) ........ ................ r83434 | georg.brandl | 2010-08-01 21:23:23 +0200 (So, 01 Aug 2010) | 108 lines Blocked revisions 83320-83323,83325-83327,83329,83339,83342-83343,83348-83351,83355,83359-83361,83371,83375,83390,83394,83399,83404,83406 via svnmerge ........ r83320 | georg.brandl | 2010-07-31 11:01:16 +0200 (Sa, 31 Jul 2010) | 1 line Update copyright years and add releases to release list. Also update Sphinx version number. ........ r83321 | georg.brandl | 2010-07-31 11:03:30 +0200 (Sa, 31 Jul 2010) | 1 line At least give IDLE 3.1 a release date. No further entries there for 3.2 though. ........ r83322 | tarek.ziade | 2010-07-31 11:10:51 +0200 (Sa, 31 Jul 2010) | 1 line reverted distutils doc to its 3.1 state ........ r83323 | georg.brandl | 2010-07-31 11:15:10 +0200 (Sa, 31 Jul 2010) | 1 line After distutils doc reversal, change back **bold todo** items to XXX comments. ........ r83325 | georg.brandl | 2010-07-31 11:37:03 +0200 (Sa, 31 Jul 2010) | 1 line Copy Sun-specific inclusion of from 2.7 maint to trunk; it seems to not have been merged to py3k. ........ r83326 | georg.brandl | 2010-07-31 12:08:09 +0200 (Sa, 31 Jul 2010) | 1 line Avoid triggering DeprecationWarnings in test_smtpd and smtpd. ........ r83327 | raymond.hettinger | 2010-07-31 12:11:39 +0200 (Sa, 31 Jul 2010) | 1 line Add functools.lfu_cache() and functools.lru_cache(). ........ r83329 | georg.brandl | 2010-07-31 12:16:21 +0200 (Sa, 31 Jul 2010) | 1 line Revert r83327. This will have to wait until after the alpha1 release. ........ r83339 | georg.brandl | 2010-07-31 13:00:47 +0200 (Sa, 31 Jul 2010) | 1 line Rewrap. ........ r83342 | georg.brandl | 2010-07-31 13:52:46 +0200 (Sa, 31 Jul 2010) | 1 line Import test_pdb with its full name, so that running python -m test.test_pdb succeeds. ........ r83343 | georg.brandl | 2010-07-31 14:06:51 +0200 (Sa, 31 Jul 2010) | 1 line From Martin: New UUIDs for the 3.2 release series. ........ r83348 | georg.brandl | 2010-07-31 20:05:35 +0200 (Sa, 31 Jul 2010) | 1 line Post-release updates. ........ r83349 | antoine.pitrou | 2010-07-31 20:08:33 +0200 (Sa, 31 Jul 2010) | 3 lines Add ssl changes to the 3.2 "what's new". ........ r83350 | georg.brandl | 2010-07-31 20:09:23 +0200 (Sa, 31 Jul 2010) | 1 line Re-commit r83327 now that the release is done. ........ r83351 | georg.brandl | 2010-07-31 20:09:46 +0200 (Sa, 31 Jul 2010) | 1 line Move news item to the correct position. ........ r83355 | georg.brandl | 2010-07-31 21:17:11 +0200 (Sa, 31 Jul 2010) | 1 line Fix bad merge: test_support -> support. ........ r83359 | georg.brandl | 2010-07-31 22:08:15 +0200 (Sa, 31 Jul 2010) | 1 line #1286: allow using fileinput.FileInput as context manager. ........ r83360 | georg.brandl | 2010-07-31 22:13:44 +0200 (Sa, 31 Jul 2010) | 1 line Clarify comment in comments test case explaining comment semantics. ........ r83361 | georg.brandl | 2010-07-31 23:04:00 +0200 (Sa, 31 Jul 2010) | 1 line #3788: more tests for http.cookies, now at 95% coverage. Also bring coding style in the module up to PEP 8, where it does not break backwards compatibility. ........ r83371 | georg.brandl | 2010-07-31 23:54:24 +0200 (Sa, 31 Jul 2010) | 1 line #8292: Fix three instances of truth tests on return values of filter() (which is always true in Python 3). ........ r83375 | antoine.pitrou | 2010-08-01 00:48:02 +0200 (So, 01 Aug 2010) | 3 lines Reorder entries by module lexicographic order ........ r83390 | georg.brandl | 2010-08-01 10:07:49 +0200 (So, 01 Aug 2010) | 1 line #8230: make Lib/test/sortperf.py run on Python 3. ........ r83394 | georg.brandl | 2010-08-01 10:46:24 +0200 (So, 01 Aug 2010) | 1 line No need to split this, there are enough long lines. ........ r83399 | georg.brandl | 2010-08-01 11:17:53 +0200 (So, 01 Aug 2010) | 1 line Package some new files that are needed for running the test suite from the MSI package. ........ r83404 | georg.brandl | 2010-08-01 16:25:22 +0200 (So, 01 Aug 2010) | 1 line #6439: fix argument type for PySys_SetArgvEx() and Py_SetProgramName() in Demo/embed code. ........ r83406 | georg.brandl | 2010-08-01 16:50:00 +0200 (So, 01 Aug 2010) | 1 line #8046: add context manager protocol support to mmap objects. Also add closed property. ........ ................ r83439 | georg.brandl | 2010-08-01 21:36:43 +0200 (So, 01 Aug 2010) | 89 lines Blocked revisions 83260,83262-83263,83266,83270,83274-83275,83283-83287,83291-83293,83308,83315-83318 via svnmerge ........ r83260 | georg.brandl | 2010-07-30 09:14:01 +0200 (Fr, 30 Jul 2010) | 1 line #4179: In pdb, allow "list ." as a command to return to the currently debugged line. ........ r83262 | georg.brandl | 2010-07-30 10:29:39 +0200 (Fr, 30 Jul 2010) | 1 line #1437051: allow "continue"/"next"/etc. in .pdbrc, also add pdb -c option to give these commands. This allows to run a script until an exception occurs. ........ r83263 | georg.brandl | 2010-07-30 10:43:32 +0200 (Fr, 30 Jul 2010) | 1 line Allow giving an explicit line number to "until". ........ r83266 | georg.brandl | 2010-07-30 11:14:20 +0200 (Fr, 30 Jul 2010) | 1 line #1472251: remove addition of "\n" to code given to pdb.run[eval](), the bug in exec() that made this necessary has been fixed. Also document that you can give code objects to run() and runeval(), and add some tests to test_pdb. ........ r83270 | georg.brandl | 2010-07-30 11:54:44 +0200 (Fr, 30 Jul 2010) | 1 line Remove redundant import. ........ r83274 | georg.brandl | 2010-07-30 13:31:03 +0200 (Fr, 30 Jul 2010) | 1 line #3143: enable "collapsible sidebar" feature of new Sphinx version. ........ r83275 | georg.brandl | 2010-07-30 14:01:20 +0200 (Fr, 30 Jul 2010) | 1 line #809887: improve pdb feedback for breakpoint-related actions. Also add a functional test for these commands. ........ r83283 | georg.brandl | 2010-07-30 16:16:43 +0200 (Fr, 30 Jul 2010) | 1 line #7964 followup: add test case to ensure issue remains fixed. ........ r83284 | georg.brandl | 2010-07-30 17:01:23 +0200 (Fr, 30 Jul 2010) | 1 line Add Breakpoint.bpformat(), which returns the info usually printed by bpprint(). Necessary for major refactoring of pdb output handling. ........ r83285 | georg.brandl | 2010-07-30 17:33:52 +0200 (Fr, 30 Jul 2010) | 1 line pdb now has its own tests. ........ r83286 | georg.brandl | 2010-07-30 18:00:46 +0200 (Fr, 30 Jul 2010) | 7 lines Several enhancements to pdb and its test suite. * added basic test for basic commands * removed duplication of command docs, and moved them to their implementation * unified and useful display of exceptions * output messages and errors using overridable methods (also fixes #1503502) ........ r83287 | georg.brandl | 2010-07-30 19:04:28 +0200 (Fr, 30 Jul 2010) | 1 line Add "longlist" and "source" commands, ideas borrowed from pdb++ by Antonio Cuni. ........ r83291 | georg.brandl | 2010-07-30 20:08:12 +0200 (Fr, 30 Jul 2010) | 1 line Fix source finding if the given frame is a module-level frame. ........ r83292 | georg.brandl | 2010-07-30 20:15:16 +0200 (Fr, 30 Jul 2010) | 1 line Test that "source" with nonexisting things works as expected. ........ r83293 | georg.brandl | 2010-07-30 20:46:38 +0200 (Fr, 30 Jul 2010) | 1 line Show the traceback line numbers as well as the current line numbers if an exception is being debugged. Courtesy of pdb++ by Antonio Cuni. Also document -> and >> markers for "list". ........ r83308 | georg.brandl | 2010-07-31 00:20:16 +0200 (Sa, 31 Jul 2010) | 1 line Part of #7245: when KeyboardInterrupt is raised while defining commands, restore the old commands instead of producing a traceback. ........ r83315 | georg.brandl | 2010-07-31 10:14:16 +0200 (Sa, 31 Jul 2010) | 1 line Fix pdb test failures on the buildbots. ........ r83316 | georg.brandl | 2010-07-31 10:20:02 +0200 (Sa, 31 Jul 2010) | 1 line Make urllib tests pass for now. Will figure out what the correct semantics should be after release. ........ r83317 | georg.brandl | 2010-07-31 10:27:46 +0200 (Sa, 31 Jul 2010) | 1 line Update pydoc topics and adapt Topics builder to Sphinx 1.0. ........ r83318 | georg.brandl | 2010-07-31 10:56:11 +0200 (Sa, 31 Jul 2010) | 1 line Bump versions and review NEWS file. ........ ................ r83446 | georg.brandl | 2010-08-01 22:54:30 +0200 (So, 01 Aug 2010) | 9 lines Recorded merge of revisions 83444 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83444 | georg.brandl | 2010-08-01 22:51:02 +0200 (So, 01 Aug 2010) | 1 line Revert r83395, it introduces test failures and is not necessary anyway since we now have to nul-terminate the string anyway. ........ ................ r83450 | georg.brandl | 2010-08-01 23:00:43 +0200 (So, 01 Aug 2010) | 12 lines Blocked revisions 83231,83236 via svnmerge ........ r83231 | georg.brandl | 2010-07-29 16:46:07 +0200 (Do, 29 Jul 2010) | 1 line #9397: remove mention of dbm.bsd which does not exist anymore. ........ r83236 | georg.brandl | 2010-07-29 19:16:10 +0200 (Do, 29 Jul 2010) | 1 line #6630: allow customizing flags for compiling string.Template.idpattern. ........ ................ r83459 | georg.brandl | 2010-08-01 23:12:00 +0200 (So, 01 Aug 2010) | 32 lines Blocked revisions 83202,83213,83217-83220,83222 via svnmerge ........ r83202 | georg.brandl | 2010-07-28 15:13:46 +0200 (Mi, 28 Jul 2010) | 1 line #1682942: add some ConfigParser features: alternate delimiters, alternate comments, empty lines in values. Also enhance the docs with more examples and mention SafeConfigParser before ConfigParser. Patch by Lukas Langa, review by myself, Eric and Ezio. ........ r83213 | georg.brandl | 2010-07-28 19:37:27 +0200 (Mi, 28 Jul 2010) | 1 line Add missing file. ........ r83217 | georg.brandl | 2010-07-29 13:15:36 +0200 (Do, 29 Jul 2010) | 1 line Remove Python 1.5 compatibility note. ........ r83218 | georg.brandl | 2010-07-29 13:49:05 +0200 (Do, 29 Jul 2010) | 1 line #6538: fix regex documentation again -- use fictional class names "regex" and "match" but do not document them as classes, remove 1.5 compat info and use new default argument syntax where possible. ........ r83219 | georg.brandl | 2010-07-29 13:56:20 +0200 (Do, 29 Jul 2010) | 1 line Fix for r83202: improve the handling of empty lines. ........ r83220 | georg.brandl | 2010-07-29 14:17:40 +0200 (Do, 29 Jul 2010) | 1 line #9411: allow selecting an encoding for configparser files. Also adds a new test config file to test special cases. ........ r83222 | georg.brandl | 2010-07-29 15:19:42 +0200 (Do, 29 Jul 2010) | 1 line Fix #9412: make list of messages an instance attribute instead of class attribute. ........ ................ r83462 | georg.brandl | 2010-08-01 23:19:50 +0200 (So, 01 Aug 2010) | 8 lines Blocked revisions 83181 via svnmerge ........ r83181 | georg.brandl | 2010-07-27 20:19:21 +0200 (Di, 27 Jul 2010) | 1 line Update Sphinx to 1.0.1. ........ ................ r83469 | georg.brandl | 2010-08-01 23:24:49 +0200 (So, 01 Aug 2010) | 8 lines Blocked revisions 83107 via svnmerge ........ r83107 | georg.brandl | 2010-07-23 18:55:42 +0200 (Fr, 23 Jul 2010) | 1 line Update to 1.0. ........ ................ r83473 | georg.brandl | 2010-08-01 23:27:58 +0200 (So, 01 Aug 2010) | 8 lines Blocked revisions 83065 via svnmerge ........ r83065 | georg.brandl | 2010-07-23 10:46:35 +0200 (Fr, 23 Jul 2010) | 1 line Use augassign. ........ ................ r83477 | georg.brandl | 2010-08-01 23:29:37 +0200 (So, 01 Aug 2010) | 12 lines Blocked revisions 82949,82964 via svnmerge ........ r82949 | georg.brandl | 2010-07-18 12:11:03 +0200 (So, 18 Jul 2010) | 1 line #9279: remove the pdb.doc file, put its contents in pdb.__doc__. Also sync this and the pdb docs, introduce a new directive for pdb commands and a role to link to them. ........ r82964 | georg.brandl | 2010-07-19 10:02:46 +0200 (Mo, 19 Jul 2010) | 1 line pydoc.pager does not promise to use $PAGER. ........ ................ r83482 | georg.brandl | 2010-08-01 23:34:07 +0200 (So, 01 Aug 2010) | 12 lines Blocked revisions 82848,82951 via svnmerge ........ r82848 | georg.brandl | 2010-07-13 08:38:10 +0200 (Di, 13 Jul 2010) | 1 line Add bytes in literal_eval doc. ........ r82951 | georg.brandl | 2010-07-18 15:43:32 +0200 (So, 18 Jul 2010) | 1 line #9110: update to ContextDecorator doc. ........ ................ r83483 | mark.dickinson | 2010-08-01 23:36:26 +0200 (So, 01 Aug 2010) | 9 lines Merged revisions 83479 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83479 | mark.dickinson | 2010-08-01 22:33:01 +0100 (Sun, 01 Aug 2010) | 1 line Don't delete Lib/test/data/README when doing 'make distclean' ........ ................ Modified: python/branches/release26-maint/ (props changed) From python-checkins at python.org Sun Aug 1 23:44:38 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 1 Aug 2010 23:44:38 +0200 (CEST) Subject: [Python-checkins] r83486 - in python/branches/release27-maint: Doc/faq/extending.rst Doc/library/re.rst Message-ID: <20100801214438.AF98AEEB03@mail.python.org> Author: georg.brandl Date: Sun Aug 1 23:44:38 2010 New Revision: 83486 Log: Merged revisions 82832,82834 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r82832 | georg.brandl | 2010-07-12 11:00:29 +0200 (Mo, 12 Jul 2010) | 1 line Take care of duplicate target name warnings. ........ r82834 | georg.brandl | 2010-07-12 11:06:13 +0200 (Mo, 12 Jul 2010) | 1 line Use raw string literals for regexes containing backlash. ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Doc/faq/extending.rst python/branches/release27-maint/Doc/library/re.rst Modified: python/branches/release27-maint/Doc/faq/extending.rst ============================================================================== --- python/branches/release27-maint/Doc/faq/extending.rst (original) +++ python/branches/release27-maint/Doc/faq/extending.rst Sun Aug 1 23:44:38 2010 @@ -26,6 +26,8 @@ C++ objects with constructors are probably not a good idea. +.. _c-wrapper-software: + Writing C is hard; are there any alternatives? ---------------------------------------------- @@ -197,11 +199,7 @@ whole lot of difference between C and C++ -- so the strategy of building a new Python type around a C structure (pointer) type will also work for C++ objects. -For C++ libraries, you can look at `SIP -`_, `CXX -`_, `Boost -`_, `Weave -`_ or `SWIG `_ +For C++ libraries, see :ref:`c-wrapper-software`. I added a module using the Setup file and the make fails; why? Modified: python/branches/release27-maint/Doc/library/re.rst ============================================================================== --- python/branches/release27-maint/Doc/library/re.rst (original) +++ python/branches/release27-maint/Doc/library/re.rst Sun Aug 1 23:44:38 2010 @@ -1210,9 +1210,9 @@ ... random.shuffle(inner_word) ... return m.group(1) + "".join(inner_word) + m.group(3) >>> text = "Professor Abdolmalek, please report your absences promptly." - >>> re.sub("(\w)(\w+)(\w)", repl, text) + >>> re.sub(r"(\w)(\w+)(\w)", repl, text) 'Poefsrosr Aealmlobdk, pslaee reorpt your abnseces plmrptoy.' - >>> re.sub("(\w)(\w+)(\w)", repl, text) + >>> re.sub(r"(\w)(\w+)(\w)", repl, text) 'Pofsroser Aodlambelk, plasee reoprt yuor asnebces potlmrpy.' From python-checkins at python.org Sun Aug 1 23:48:47 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 1 Aug 2010 23:48:47 +0200 (CEST) Subject: [Python-checkins] r83487 - in python/branches/release26-maint: Doc/documenting/markup.rst Doc/faq/extending.rst Doc/install/index.rst Doc/library/configparser.rst Doc/library/htmlparser.rst Doc/library/itertools.rst Doc/library/locale.rst Doc/library/re.rst Doc/library/select.rst Doc/library/urllib2.rst Doc/tutorial/classes.rst Doc/whatsnew/2.0.rst Doc/whatsnew/2.5.rst Lib/ConfigParser.py Misc/developers.txt Objects/unicodeobject.c Message-ID: <20100801214847.DFD32EE987@mail.python.org> Author: georg.brandl Date: Sun Aug 1 23:48:47 2010 New Revision: 83487 Log: Merged revisions 83452,83457,83466,83471,83475,83480,83486 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/release27-maint ................ r83452 | georg.brandl | 2010-08-01 23:06:46 +0200 (So, 01 Aug 2010) | 25 lines Merged revisions 83226-83227,83229-83230,83232 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83226 | georg.brandl | 2010-07-29 16:17:12 +0200 (Do, 29 Jul 2010) | 1 line #1090076: explain the behavior of *vars* in get() better. ........ r83227 | georg.brandl | 2010-07-29 16:23:06 +0200 (Do, 29 Jul 2010) | 1 line Use Py_CLEAR(). ........ r83229 | georg.brandl | 2010-07-29 16:32:22 +0200 (Do, 29 Jul 2010) | 1 line #9407: document configparser.Error. ........ r83230 | georg.brandl | 2010-07-29 16:36:11 +0200 (Do, 29 Jul 2010) | 1 line Use correct directive and name. ........ r83232 | georg.brandl | 2010-07-29 16:49:08 +0200 (Do, 29 Jul 2010) | 1 line #9388: remove ERA_YEAR which is never defined in the source code. ........ ................ r83457 | georg.brandl | 2010-08-01 23:10:57 +0200 (So, 01 Aug 2010) | 9 lines Merged revisions 83223 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83223 | georg.brandl | 2010-07-29 15:38:37 +0200 (Do, 29 Jul 2010) | 1 line #3874: document HTMLParser.unknown_decl(). ........ ................ r83466 | georg.brandl | 2010-08-01 23:23:50 +0200 (So, 01 Aug 2010) | 29 lines Merged revisions 83160-83161,83166,83168-83169,83171 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83160 | georg.brandl | 2010-07-26 10:51:42 +0200 (Mo, 26 Jul 2010) | 1 line #9381: fix markup. ........ r83161 | georg.brandl | 2010-07-26 11:33:12 +0200 (Mo, 26 Jul 2010) | 1 line Add Brian Quinlan. ........ r83166 | georg.brandl | 2010-07-26 17:11:49 +0200 (Mo, 26 Jul 2010) | 1 line Fix grammar. ........ r83168 | georg.brandl | 2010-07-26 19:00:20 +0200 (Mo, 26 Jul 2010) | 1 line Fix indentation in example. ........ r83169 | georg.brandl | 2010-07-26 19:09:32 +0200 (Mo, 26 Jul 2010) | 1 line Add Reid. ........ r83171 | georg.brandl | 2010-07-26 23:12:13 +0200 (Mo, 26 Jul 2010) | 1 line Clarify. ........ ................ r83471 | georg.brandl | 2010-08-01 23:26:45 +0200 (So, 01 Aug 2010) | 9 lines Merged revisions 83106 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83106 | georg.brandl | 2010-07-23 18:55:26 +0200 (Fr, 23 Jul 2010) | 1 line Fix some markup glitches. ........ ................ r83475 | georg.brandl | 2010-08-01 23:28:47 +0200 (So, 01 Aug 2010) | 9 lines Merged revisions 82965 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r82965 | georg.brandl | 2010-07-19 13:28:05 +0200 (Mo, 19 Jul 2010) | 1 line Clarification. Yay importlib! ........ ................ r83480 | georg.brandl | 2010-08-01 23:33:42 +0200 (So, 01 Aug 2010) | 17 lines Merged revisions 82871,82960-82961 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r82871 | georg.brandl | 2010-07-14 10:00:22 +0200 (Mi, 14 Jul 2010) | 1 line #9258: fix copy-paste errors. ........ r82960 | georg.brandl | 2010-07-19 08:52:35 +0200 (Mo, 19 Jul 2010) | 1 line Clarify. ........ r82961 | georg.brandl | 2010-07-19 08:57:52 +0200 (Mo, 19 Jul 2010) | 1 line Clarify :option: description. ........ ................ r83486 | georg.brandl | 2010-08-01 23:44:38 +0200 (So, 01 Aug 2010) | 13 lines Merged revisions 82832,82834 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r82832 | georg.brandl | 2010-07-12 11:00:29 +0200 (Mo, 12 Jul 2010) | 1 line Take care of duplicate target name warnings. ........ r82834 | georg.brandl | 2010-07-12 11:06:13 +0200 (Mo, 12 Jul 2010) | 1 line Use raw string literals for regexes containing backlash. ........ ................ Modified: python/branches/release26-maint/ (props changed) python/branches/release26-maint/Doc/documenting/markup.rst python/branches/release26-maint/Doc/faq/extending.rst python/branches/release26-maint/Doc/install/index.rst python/branches/release26-maint/Doc/library/configparser.rst python/branches/release26-maint/Doc/library/htmlparser.rst python/branches/release26-maint/Doc/library/itertools.rst python/branches/release26-maint/Doc/library/locale.rst python/branches/release26-maint/Doc/library/re.rst python/branches/release26-maint/Doc/library/select.rst python/branches/release26-maint/Doc/library/urllib2.rst python/branches/release26-maint/Doc/tutorial/classes.rst python/branches/release26-maint/Doc/whatsnew/2.0.rst python/branches/release26-maint/Doc/whatsnew/2.5.rst python/branches/release26-maint/Lib/ConfigParser.py python/branches/release26-maint/Misc/developers.txt python/branches/release26-maint/Objects/unicodeobject.c Modified: python/branches/release26-maint/Doc/documenting/markup.rst ============================================================================== --- python/branches/release26-maint/Doc/documenting/markup.rst (original) +++ python/branches/release26-maint/Doc/documenting/markup.rst Sun Aug 1 23:48:47 2010 @@ -200,8 +200,8 @@ .. describe:: cmdoption - Describes a command line option or switch. Option argument names should be - enclosed in angle brackets. Example:: + Describes a Python command line option or switch. Option argument names + should be enclosed in angle brackets. Example:: .. cmdoption:: -m @@ -502,8 +502,9 @@ .. describe:: option - A command-line option to an executable program. The leading hyphen(s) must - be included. + A command-line option of Python. The leading hyphen(s) must be included. + If a matching ``cmdoption`` directive exists, it is linked to. For options + of other programs or scripts, use simple ````code```` markup. .. describe:: program Modified: python/branches/release26-maint/Doc/faq/extending.rst ============================================================================== --- python/branches/release26-maint/Doc/faq/extending.rst (original) +++ python/branches/release26-maint/Doc/faq/extending.rst Sun Aug 1 23:48:47 2010 @@ -26,6 +26,8 @@ C++ objects with constructors are probably not a good idea. +.. _c-wrapper-software: + Writing C is hard; are there any alternatives? ---------------------------------------------- @@ -197,11 +199,7 @@ whole lot of difference between C and C++ -- so the strategy of building a new Python type around a C structure (pointer) type will also work for C++ objects. -For C++ libraries, you can look at `SIP -`_, `CXX -`_, `Boost -`_, `Weave -`_ or `SWIG `_ +For C++ libraries, see :ref:`c-wrapper-software`. I added a module using the Setup file and the make fails; why? Modified: python/branches/release26-maint/Doc/install/index.rst ============================================================================== --- python/branches/release26-maint/Doc/install/index.rst (original) +++ python/branches/release26-maint/Doc/install/index.rst Sun Aug 1 23:48:47 2010 @@ -314,8 +314,8 @@ stash of Python modules. This scheme's name is derived from the idea of a "home" directory on Unix, since it's not unusual for a Unix user to make their home directory have a layout similar to :file:`/usr/` or :file:`/usr/local/`. -This scheme can be used by anyone, regardless of the operating system their -installing for. +This scheme can be used by anyone, regardless of the operating system they +are installing for. Installing a new module distribution is as simple as :: Modified: python/branches/release26-maint/Doc/library/configparser.rst ============================================================================== --- python/branches/release26-maint/Doc/library/configparser.rst (original) +++ python/branches/release26-maint/Doc/library/configparser.rst Sun Aug 1 23:48:47 2010 @@ -41,6 +41,18 @@ provided on initialization and retrieval. Lines beginning with ``'#'`` or ``';'`` are ignored and may be used to provide comments. +Configuration files may include comments, prefixed by specific characters (``#`` +and ``;``). Comments may appear on their own in an otherwise empty line, or may +be entered in lines holding values or spection names. In the latter case, they +need to be preceded by a whitespace character to be recognized as a comment. +(For backwards compatibility, only ``;`` starts an inline comment, while ``#`` +does not.) + +On top of the core functionality, :class:`SafeConfigParser` supports +interpolation. This means values can contain format strings which refer to +other values in the same section, or values in a special ``DEFAULT`` section. +Additional defaults can be provided on initialization. + For example:: [My Section] @@ -104,6 +116,11 @@ .. versionadded:: 2.3 +.. exception:: Error + + Base class for all other configparser exceptions. + + .. exception:: NoSectionError Exception raised when a specified section is not found. @@ -347,11 +364,13 @@ .. method:: ConfigParser.get(section, option[, raw[, vars]]) - Get an *option* value for the named *section*. All the ``'%'`` interpolations - are expanded in the return values, based on the defaults passed into the - constructor, as well as the options *vars* provided, unless the *raw* argument - is true. - + Get an *option* value for the named *section*. If *vars* is provided, it + must be a dictionary. The *option* is looked up in *vars* (if provided), + *section*, and in *defaults* in that order. + + All the ``'%'`` interpolations are expanded in the return values, unless the + *raw* argument is true. Values for interpolation keys are looked up in the + same manner as the option. .. method:: ConfigParser.items(section[, raw[, vars]]) Modified: python/branches/release26-maint/Doc/library/htmlparser.rst ============================================================================== --- python/branches/release26-maint/Doc/library/htmlparser.rst (original) +++ python/branches/release26-maint/Doc/library/htmlparser.rst Sun Aug 1 23:48:47 2010 @@ -148,10 +148,18 @@ .. method:: HTMLParser.handle_decl(decl) - Method called when an SGML declaration is read by the parser. The *decl* - parameter will be the entire contents of the declaration inside the ```` markup. It is intended to be overridden by a derived class; the base - class implementation does nothing. + Method called when an SGML ``doctype`` declaration is read by the parser. + The *decl* parameter will be the entire contents of the declaration inside + the ```` markup. It is intended to be overridden by a derived class; + the base class implementation does nothing. + + +.. method:: HTMLParser.unknown_decl(data) + + Method called when an unrecognized SGML declaration is read by the parser. + The *data* parameter will be the entire contents of the declaration inside + the ```` markup. It is sometimes useful to be be overridden by a + derived class; the base class implementation throws an :exc:`HTMLParseError`. .. method:: HTMLParser.handle_pi(data) Modified: python/branches/release26-maint/Doc/library/itertools.rst ============================================================================== --- python/branches/release26-maint/Doc/library/itertools.rst (original) +++ python/branches/release26-maint/Doc/library/itertools.rst Sun Aug 1 23:48:47 2010 @@ -101,7 +101,7 @@ yield element -.. function:: itertools.chain.from_iterable(iterable) +.. classmethod:: chain.from_iterable(iterable) Alternate constructor for :func:`chain`. Gets chained inputs from a single iterable argument that is evaluated lazily. Equivalent to:: Modified: python/branches/release26-maint/Doc/library/locale.rst ============================================================================== --- python/branches/release26-maint/Doc/library/locale.rst (original) +++ python/branches/release26-maint/Doc/library/locale.rst Sun Aug 1 23:48:47 2010 @@ -248,10 +248,6 @@ specified, and therefore you should not assume knowledge of it on different systems. - .. data:: ERA_YEAR - - Get the year in the relevant era of the locale. - .. data:: ERA_D_T_FMT Get a format string for :func:`strftime` to represent dates and times in a Modified: python/branches/release26-maint/Doc/library/re.rst ============================================================================== --- python/branches/release26-maint/Doc/library/re.rst (original) +++ python/branches/release26-maint/Doc/library/re.rst Sun Aug 1 23:48:47 2010 @@ -1198,9 +1198,9 @@ ... random.shuffle(inner_word) ... return m.group(1) + "".join(inner_word) + m.group(3) >>> text = "Professor Abdolmalek, please report your absences promptly." - >>> re.sub("(\w)(\w+)(\w)", repl, text) + >>> re.sub(r"(\w)(\w+)(\w)", repl, text) 'Poefsrosr Aealmlobdk, pslaee reorpt your abnseces plmrptoy.' - >>> re.sub("(\w)(\w+)(\w)", repl, text) + >>> re.sub(r"(\w)(\w+)(\w)", repl, text) 'Pofsroser Aodlambelk, plasee reoprt yuor asnebces potlmrpy.' Modified: python/branches/release26-maint/Doc/library/select.rst ============================================================================== --- python/branches/release26-maint/Doc/library/select.rst (original) +++ python/branches/release26-maint/Doc/library/select.rst Sun Aug 1 23:48:47 2010 @@ -44,7 +44,7 @@ .. function:: kqueue() - (Only supported on BSD.) Returns a kernel queue object object; see section + (Only supported on BSD.) Returns a kernel queue object; see section :ref:`kqueue-objects` below for the methods supported by kqueue objects. .. versionadded:: 2.6 @@ -52,8 +52,8 @@ .. function:: kevent(ident, filter=KQ_FILTER_READ, flags=KQ_EV_ADD, fflags=0, data=0, udata=0) - (Only supported on BSD.) Returns a kernel event object object; see section - :ref:`kevent-objects` below for the methods supported by kqueue objects. + (Only supported on BSD.) Returns a kernel event object; see section + :ref:`kevent-objects` below for the methods supported by kevent objects. .. versionadded:: 2.6 Modified: python/branches/release26-maint/Doc/library/urllib2.rst ============================================================================== --- python/branches/release26-maint/Doc/library/urllib2.rst (original) +++ python/branches/release26-maint/Doc/library/urllib2.rst Sun Aug 1 23:48:47 2010 @@ -464,7 +464,8 @@ named :meth:`unknown_open`. Note that the implementation of these methods may involve calls of the parent - :class:`OpenerDirector` instance's :meth:`.open` and :meth:`.error` methods. + :class:`OpenerDirector` instance's :meth:`~OpenerDirector.open` and + :meth:`~OpenerDirector.error` methods. #. Every handler with a method named like :samp:`{protocol}_response` has that method called to post-process the response. Modified: python/branches/release26-maint/Doc/tutorial/classes.rst ============================================================================== --- python/branches/release26-maint/Doc/tutorial/classes.rst (original) +++ python/branches/release26-maint/Doc/tutorial/classes.rst Sun Aug 1 23:48:47 2010 @@ -693,7 +693,7 @@ StopIteration Having seen the mechanics behind the iterator protocol, it is easy to add -iterator behavior to your classes. Define a :meth:`__iter__` method which +iterator behavior to your classes. Define an :meth:`__iter__` method which returns an object with a :meth:`next` method. If the class defines :meth:`next`, then :meth:`__iter__` can just return ``self``:: @@ -710,7 +710,10 @@ self.index = self.index - 1 return self.data[self.index] - >>> for char in Reverse('spam'): + >>> rev = Reverse('spam') + >>> iter(rev) + <__main__.Reverse object at 0x00A1DB50> + >>> for char in rev: ... print char ... m Modified: python/branches/release26-maint/Doc/whatsnew/2.0.rst ============================================================================== --- python/branches/release26-maint/Doc/whatsnew/2.0.rst (original) +++ python/branches/release26-maint/Doc/whatsnew/2.0.rst Sun Aug 1 23:48:47 2010 @@ -656,7 +656,7 @@ The change which will probably break the most code is tightening up the arguments accepted by some methods. Some methods would take multiple arguments and treat them as a tuple, particularly various list methods such as -:meth:`.append` and :meth:`.insert`. In earlier versions of Python, if ``L`` is +:meth:`append` and :meth:`insert`. In earlier versions of Python, if ``L`` is a list, ``L.append( 1,2 )`` appends the tuple ``(1,2)`` to the list. In Python 2.0 this causes a :exc:`TypeError` exception to be raised, with the message: 'append requires exactly 1 argument; 2 given'. The fix is to simply add an Modified: python/branches/release26-maint/Doc/whatsnew/2.5.rst ============================================================================== --- python/branches/release26-maint/Doc/whatsnew/2.5.rst (original) +++ python/branches/release26-maint/Doc/whatsnew/2.5.rst Sun Aug 1 23:48:47 2010 @@ -1765,7 +1765,7 @@ http://effbot.org/zone/element-index.htm. ElementTree represents an XML document as a tree of element nodes. The text -content of the document is stored as the :attr:`.text` and :attr:`.tail` +content of the document is stored as the :attr:`text` and :attr:`tail` attributes of (This is one of the major differences between ElementTree and the Document Object Model; in the DOM there are many different types of node, including :class:`TextNode`.) Modified: python/branches/release26-maint/Lib/ConfigParser.py ============================================================================== --- python/branches/release26-maint/Lib/ConfigParser.py (original) +++ python/branches/release26-maint/Lib/ConfigParser.py Sun Aug 1 23:48:47 2010 @@ -515,11 +515,12 @@ def get(self, section, option, raw=False, vars=None): """Get an option value for a given section. - All % interpolations are expanded in the return values, based on the - defaults passed into the constructor, unless the optional argument - `raw' is true. Additional substitutions may be provided using the - `vars' argument, which must be a dictionary whose contents overrides - any pre-existing defaults. + If `vars' is provided, it must be a dictionary. The option is looked up + in `vars' (if provided), `section', and in `defaults' in that order. + + All % interpolations are expanded in the return values, unless the + optional argument `raw' is true. Values for interpolation keys are + looked up in the same manner as the option. The section DEFAULT is special. """ Modified: python/branches/release26-maint/Misc/developers.txt ============================================================================== --- python/branches/release26-maint/Misc/developers.txt (original) +++ python/branches/release26-maint/Misc/developers.txt Sun Aug 1 23:48:47 2010 @@ -17,6 +17,16 @@ Permissions History ------------------- +- Brian Quinlan was given commit access on Jul 26 2010 by GFB, + for work related to PEP 3148. + +- Reid Kleckner was given commit access on Jul 11 2010 by GFB, + for work on the py3k-jit branch, at suggestion of the Unladen + Swallow team. + +- Alexander Belopolsky was given commit access on May 25 2010 + by MvL at suggestion of Mark Dickinson. + - Tim Golden was given commit access on April 21 2010 by MvL, at suggestion of Michael Foord. Modified: python/branches/release26-maint/Objects/unicodeobject.c ============================================================================== --- python/branches/release26-maint/Objects/unicodeobject.c (original) +++ python/branches/release26-maint/Objects/unicodeobject.c Sun Aug 1 23:48:47 2010 @@ -272,8 +272,7 @@ reset: /* Reset the object caches */ if (unicode->defenc) { - Py_DECREF(unicode->defenc); - unicode->defenc = NULL; + Py_CLEAR(unicode->defenc); } unicode->hash = -1; @@ -371,8 +370,7 @@ unicode->length = 0; } if (unicode->defenc) { - Py_DECREF(unicode->defenc); - unicode->defenc = NULL; + Py_CLEAR(unicode->defenc); } /* Add to free list */ *(PyUnicodeObject **)unicode = free_list; From python-checkins at python.org Sun Aug 1 23:50:39 2010 From: python-checkins at python.org (raymond.hettinger) Date: Sun, 1 Aug 2010 23:50:39 +0200 (CEST) Subject: [Python-checkins] r83488 - python/branches/py3k/PC/VS8.0/pythoncore.vcproj Message-ID: <20100801215039.0AAEDEE992@mail.python.org> Author: raymond.hettinger Date: Sun Aug 1 23:50:38 2010 New Revision: 83488 Log: Fix VS8.0 build by adding _time.h and _time.c to the project file. Modified: python/branches/py3k/PC/VS8.0/pythoncore.vcproj Modified: python/branches/py3k/PC/VS8.0/pythoncore.vcproj ============================================================================== --- python/branches/py3k/PC/VS8.0/pythoncore.vcproj (original) +++ python/branches/py3k/PC/VS8.0/pythoncore.vcproj Sun Aug 1 23:50:38 2010 @@ -943,6 +943,10 @@ > + + @@ -1167,6 +1171,10 @@ > + + From python-checkins at python.org Sun Aug 1 23:58:18 2010 From: python-checkins at python.org (georg.brandl) Date: Sun, 1 Aug 2010 23:58:18 +0200 (CEST) Subject: [Python-checkins] r83489 - in python/branches/release26-maint: Lib/asyncore.py Lib/test/test_asyncore.py Misc/ACKS Misc/NEWS Message-ID: <20100801215818.D6A95EEA1B@mail.python.org> Author: georg.brandl Date: Sun Aug 1 23:58:18 2010 New Revision: 83489 Log: Merged revisions 83461 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/release27-maint ................ r83461 | georg.brandl | 2010-08-01 23:18:52 +0200 (So, 01 Aug 2010) | 9 lines Merged revisions 83201 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83201 | georg.brandl | 2010-07-28 10:19:35 +0200 (Mi, 28 Jul 2010) | 1 line #9354: Provide getsockopt() in asyncore file_wrapper(). Patch by Lukas Langa. ........ ................ Modified: python/branches/release26-maint/ (props changed) python/branches/release26-maint/Lib/asyncore.py python/branches/release26-maint/Lib/test/test_asyncore.py python/branches/release26-maint/Misc/ACKS python/branches/release26-maint/Misc/NEWS Modified: python/branches/release26-maint/Lib/asyncore.py ============================================================================== --- python/branches/release26-maint/Lib/asyncore.py (original) +++ python/branches/release26-maint/Lib/asyncore.py Sun Aug 1 23:58:18 2010 @@ -594,6 +594,14 @@ def send(self, *args): return os.write(self.fd, *args) + def getsockopt(self, level, optname, buflen=None): + if (level == socket.SOL_SOCKET and + optname == socket.SO_ERROR and + not buflen): + return 0 + raise NotImplementedError("Only asyncore specific behaviour " + "implemented.") + read = recv write = send Modified: python/branches/release26-maint/Lib/test/test_asyncore.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_asyncore.py (original) +++ python/branches/release26-maint/Lib/test/test_asyncore.py Sun Aug 1 23:58:18 2010 @@ -412,6 +412,17 @@ w.close() self.assertEqual(file(TESTFN).read(), self.d + d1 + d2) + def test_dispatcher(self): + fd = os.open(TESTFN, os.O_RDONLY) + data = [] + class FileDispatcher(asyncore.file_dispatcher): + def handle_read(self): + data.append(self.recv(29)) + s = FileDispatcher(fd) + os.close(fd) + asyncore.loop(timeout=0.01, use_poll=True, count=2) + self.assertEqual("".join(data), self.d) + def test_main(): tests = [HelperFunctionTests, DispatcherTests, DispatcherWithSendTests, Modified: python/branches/release26-maint/Misc/ACKS ============================================================================== --- python/branches/release26-maint/Misc/ACKS (original) +++ python/branches/release26-maint/Misc/ACKS Sun Aug 1 23:58:18 2010 @@ -407,6 +407,7 @@ Andrew Kuchling Vladimir Kushnir Cameron Laird +?ukasz Langa Tino Lange Andrew Langmead Detlef Lannert Modified: python/branches/release26-maint/Misc/NEWS ============================================================================== --- python/branches/release26-maint/Misc/NEWS (original) +++ python/branches/release26-maint/Misc/NEWS Sun Aug 1 23:58:18 2010 @@ -35,6 +35,8 @@ when turned into an exception: in this case the exception simply gets ignored. +- Issue #9354: Provide getsockopt() in asyncore's file_wrapper. + - In the unicode/str.format(), raise a ValueError when indexes to arguments are too large. From python-checkins at python.org Mon Aug 2 00:00:39 2010 From: python-checkins at python.org (georg.brandl) Date: Mon, 2 Aug 2010 00:00:39 +0200 (CEST) Subject: [Python-checkins] r83490 - in python/branches/release26-maint: Lib/robotparser.py Lib/test/test_robotparser.py Misc/NEWS Message-ID: <20100801220039.97FBFEEAA7@mail.python.org> Author: georg.brandl Date: Mon Aug 2 00:00:39 2010 New Revision: 83490 Log: Merged revisions 83449 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/release27-maint ................ r83449 | georg.brandl | 2010-08-01 22:59:03 +0200 (So, 01 Aug 2010) | 9 lines Merged revisions 83238 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83238 | georg.brandl | 2010-07-29 19:55:01 +0200 (Do, 29 Jul 2010) | 1 line #4108: the first default entry (User-agent: *) wins. ........ ................ Modified: python/branches/release26-maint/ (props changed) python/branches/release26-maint/Lib/robotparser.py python/branches/release26-maint/Lib/test/test_robotparser.py python/branches/release26-maint/Misc/NEWS Modified: python/branches/release26-maint/Lib/robotparser.py ============================================================================== --- python/branches/release26-maint/Lib/robotparser.py (original) +++ python/branches/release26-maint/Lib/robotparser.py Mon Aug 2 00:00:39 2010 @@ -68,7 +68,9 @@ def _add_entry(self, entry): if "*" in entry.useragents: # the default entry is considered last - self.default_entry = entry + if self.default_entry is None: + # the first default entry wins + self.default_entry = entry else: self.entries.append(entry) @@ -120,7 +122,7 @@ entry.rulelines.append(RuleLine(line[1], True)) state = 2 if state == 2: - self.entries.append(entry) + self._add_entry(entry) def can_fetch(self, useragent, url): Modified: python/branches/release26-maint/Lib/test/test_robotparser.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_robotparser.py (original) +++ python/branches/release26-maint/Lib/test/test_robotparser.py Mon Aug 2 00:00:39 2010 @@ -202,6 +202,20 @@ RobotTest(13, doc, good, bad, agent="googlebot") +# 14. For issue #4108 (obey first * entry) +doc = """ +User-agent: * +Disallow: /some/path + +User-agent: * +Disallow: /another/path +""" + +good = ['/another/path'] +bad = ['/some/path'] + +RobotTest(14, doc, good, bad) + class TestCase(unittest.TestCase): def runTest(self): Modified: python/branches/release26-maint/Misc/NEWS ============================================================================== --- python/branches/release26-maint/Misc/NEWS (original) +++ python/branches/release26-maint/Misc/NEWS Mon Aug 2 00:00:39 2010 @@ -35,6 +35,9 @@ when turned into an exception: in this case the exception simply gets ignored. +- Issue #4108: In urllib.robotparser, if there are multiple 'User-agent: *' + entries, consider the first one. + - Issue #9354: Provide getsockopt() in asyncore's file_wrapper. - In the unicode/str.format(), raise a ValueError when indexes to arguments are From python-checkins at python.org Mon Aug 2 00:02:10 2010 From: python-checkins at python.org (georg.brandl) Date: Mon, 2 Aug 2010 00:02:10 +0200 (CEST) Subject: [Python-checkins] r83491 - in python/branches/release26-maint: Lib/hotshot/log.py Lib/test/test_hotshot.py Misc/NEWS Objects/stringobject.c Message-ID: <20100801220210.1FE8CEEA47@mail.python.org> Author: georg.brandl Date: Mon Aug 2 00:02:09 2010 New Revision: 83491 Log: Merged revisions 83354,83365 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/release27-maint ........ r83354 | georg.brandl | 2010-07-31 21:07:37 +0200 (Sa, 31 Jul 2010) | 1 line #9328: string format methods return strings. ........ r83365 | georg.brandl | 2010-07-31 23:22:36 +0200 (Sa, 31 Jul 2010) | 1 line #1019882: if start() and stop() were not in the same stack frame, stats.load() would crash with IndexError. ........ Modified: python/branches/release26-maint/ (props changed) python/branches/release26-maint/Lib/hotshot/log.py python/branches/release26-maint/Lib/test/test_hotshot.py python/branches/release26-maint/Misc/NEWS python/branches/release26-maint/Objects/stringobject.c Modified: python/branches/release26-maint/Lib/hotshot/log.py ============================================================================== --- python/branches/release26-maint/Lib/hotshot/log.py (original) +++ python/branches/release26-maint/Lib/hotshot/log.py Mon Aug 2 00:02:09 2010 @@ -106,7 +106,10 @@ return what, t, tdelta if what == WHAT_EXIT: - return what, self._pop(), tdelta + try: + return what, self._pop(), tdelta + except IndexError: + raise StopIteration if what == WHAT_LINENO: filename, firstlineno, funcname = self._stack[-1] Modified: python/branches/release26-maint/Lib/test/test_hotshot.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_hotshot.py (original) +++ python/branches/release26-maint/Lib/test/test_hotshot.py Mon Aug 2 00:02:09 2010 @@ -10,6 +10,7 @@ from test import test_support from hotshot.log import ENTER, EXIT, LINE +from hotshot import stats def shortfilename(fn): @@ -136,6 +137,19 @@ emptyfile.close() gc.collect() + def test_load_stats(self): + def start(prof): + prof.start() + # Make sure stats can be loaded when start and stop of profiler + # are not executed in the same stack frame. + profiler = self.new_profiler() + start(profiler) + profiler.stop() + profiler.close() + stats.load(self.logfn) + os.unlink(self.logfn) + + def test_main(): test_support.run_unittest(HotShotTestCase) Modified: python/branches/release26-maint/Misc/NEWS ============================================================================== --- python/branches/release26-maint/Misc/NEWS (original) +++ python/branches/release26-maint/Misc/NEWS Mon Aug 2 00:02:09 2010 @@ -89,6 +89,8 @@ Library ------- +- Issue #1019882: Fix IndexError when loading certain hotshot stats. + - Issue #8397: Raise an error when attempting to mix iteration and regular reads on a BZ2File object, rather than returning incorrect results. Modified: python/branches/release26-maint/Objects/stringobject.c ============================================================================== --- python/branches/release26-maint/Objects/stringobject.c (original) +++ python/branches/release26-maint/Objects/stringobject.c Mon Aug 2 00:02:09 2010 @@ -3959,7 +3959,7 @@ #include "stringlib/string_format.h" PyDoc_STRVAR(format__doc__, -"S.format(*args, **kwargs) -> unicode\n\ +"S.format(*args, **kwargs) -> string\n\ \n\ "); @@ -3993,7 +3993,7 @@ } PyDoc_STRVAR(p_format__doc__, -"S.__format__(format_spec) -> unicode\n\ +"S.__format__(format_spec) -> string\n\ \n\ "); From python-checkins at python.org Mon Aug 2 00:05:31 2010 From: python-checkins at python.org (georg.brandl) Date: Mon, 2 Aug 2010 00:05:31 +0200 (CEST) Subject: [Python-checkins] r83492 - in python/branches/release26-maint: Lib/_MozillaCookieJar.py Lib/doctest.py Lib/imaplib.py Lib/pydoc.py Lib/test/test_doctest.py Makefile.pre.in Misc/NEWS Message-ID: <20100801220531.5F816EE997@mail.python.org> Author: georg.brandl Date: Mon Aug 2 00:05:31 2010 New Revision: 83492 Log: Merged revisions 83392,83426 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/release27-maint ................ r83392 | georg.brandl | 2010-08-01 10:22:05 +0200 (So, 01 Aug 2010) | 1 line #8471: reset _SpoofOut.buf to an empty string when truncating; if Unicode had been output previously, it had been coerced to a Unicode string, potentially making subsequent prints behave differently or raise UnicodeErrors. ................ r83426 | georg.brandl | 2010-08-01 21:06:51 +0200 (So, 01 Aug 2010) | 27 lines Merged revisions 83370,83372-83374,83384 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83370 | georg.brandl | 2010-07-31 23:51:48 +0200 (Sa, 31 Jul 2010) | 5 lines #8198: the Helper class should not save the stdin and stdout objects at import time, rather by default use the current streams like the other APIs that output help. ........ r83372 | georg.brandl | 2010-08-01 00:05:54 +0200 (So, 01 Aug 2010) | 1 line #4007: remove *.a and *.so.X.Y files in "make clean". ........ r83373 | georg.brandl | 2010-08-01 00:11:11 +0200 (So, 01 Aug 2010) | 1 line #5147: revert accidental indentation of header constant for MozillaCookieJar. ........ r83374 | georg.brandl | 2010-08-01 00:32:52 +0200 (So, 01 Aug 2010) | 1 line #5146: handle UID THREAD command correctly. ........ r83384 | georg.brandl | 2010-08-01 08:32:55 +0200 (So, 01 Aug 2010) | 1 line Build properties using lambdas. This makes test_pyclbr pass again, because it does not think that input and output are methods anymore. ........ ................ Modified: python/branches/release26-maint/ (props changed) python/branches/release26-maint/Lib/_MozillaCookieJar.py python/branches/release26-maint/Lib/doctest.py python/branches/release26-maint/Lib/imaplib.py python/branches/release26-maint/Lib/pydoc.py python/branches/release26-maint/Lib/test/test_doctest.py python/branches/release26-maint/Makefile.pre.in python/branches/release26-maint/Misc/NEWS Modified: python/branches/release26-maint/Lib/_MozillaCookieJar.py ============================================================================== --- python/branches/release26-maint/Lib/_MozillaCookieJar.py (original) +++ python/branches/release26-maint/Lib/_MozillaCookieJar.py Mon Aug 2 00:05:31 2010 @@ -38,9 +38,9 @@ """ magic_re = "#( Netscape)? HTTP Cookie File" header = """\ - # Netscape HTTP Cookie File - # http://www.netscape.com/newsref/std/cookie_spec.html - # This is a generated file! Do not edit. +# Netscape HTTP Cookie File +# http://www.netscape.com/newsref/std/cookie_spec.html +# This is a generated file! Do not edit. """ Modified: python/branches/release26-maint/Lib/doctest.py ============================================================================== --- python/branches/release26-maint/Lib/doctest.py (original) +++ python/branches/release26-maint/Lib/doctest.py Mon Aug 2 00:05:31 2010 @@ -263,6 +263,9 @@ StringIO.truncate(self, size) if hasattr(self, "softspace"): del self.softspace + if not self.buf: + # Reset it to an empty string, to make sure it's not unicode. + self.buf = '' # Worst-case linear-time ellipsis matching. def _ellipsis_match(want, got): Modified: python/branches/release26-maint/Lib/imaplib.py ============================================================================== --- python/branches/release26-maint/Lib/imaplib.py (original) +++ python/branches/release26-maint/Lib/imaplib.py Mon Aug 2 00:05:31 2010 @@ -751,7 +751,7 @@ ', '.join(Commands[command]))) name = 'UID' typ, dat = self._simple_command(name, command, *args) - if command in ('SEARCH', 'SORT'): + if command in ('SEARCH', 'SORT', 'THREAD'): name = command else: name = 'FETCH' Modified: python/branches/release26-maint/Lib/pydoc.py ============================================================================== --- python/branches/release26-maint/Lib/pydoc.py (original) +++ python/branches/release26-maint/Lib/pydoc.py Mon Aug 2 00:05:31 2010 @@ -1705,9 +1705,12 @@ 'CONTEXTMANAGERS': ('context-managers', 'with'), } - def __init__(self, input, output): - self.input = input - self.output = output + def __init__(self, input=None, output=None): + self._input = input + self._output = output + + input = property(lambda self: self._input or sys.stdin) + output = property(lambda self: self._output or sys.stdout) def __repr__(self): if inspect.stack()[1][3] == '?': @@ -1884,7 +1887,7 @@ for modules whose descriptions contain the word "spam". ''') -help = Helper(sys.stdin, sys.stdout) +help = Helper() class Scanner: """A generic tree iterator.""" Modified: python/branches/release26-maint/Lib/test/test_doctest.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_doctest.py (original) +++ python/branches/release26-maint/Lib/test/test_doctest.py Mon Aug 2 00:05:31 2010 @@ -1508,7 +1508,33 @@ >>> test = doctest.DocTestParser().get_doctest(s, {}, 's', 's.py', 0) Traceback (most recent call last): ValueError: line 0 of the doctest for s has an option directive on a line with no example: '# doctest: +ELLIPSIS' -""" + + """ + + def test_unicode_output(self): r""" + +Check that unicode output works: + + >>> u'\xe9' + u'\xe9' + +If we return unicode, SpoofOut's buf variable becomes automagically +converted to unicode. This means all subsequent output becomes converted +to unicode, and if the output contains non-ascii characters that failed. +It used to be that this state change carried on between tests, meaning +tests would fail if unicode has been output previously in the testrun. +This test tests that this is no longer so: + + >>> print u'abc' + abc + +And then return a string with non-ascii characters: + + >>> print u'\xe9'.encode('utf-8') + ? + + """ + def test_testsource(): r""" Unit tests for `testsource()`. Modified: python/branches/release26-maint/Makefile.pre.in ============================================================================== --- python/branches/release26-maint/Makefile.pre.in (original) +++ python/branches/release26-maint/Makefile.pre.in Mon Aug 2 00:05:31 2010 @@ -1137,10 +1137,11 @@ find $(srcdir) -name '*.py[co]' -exec rm -f {} ';' clean: pycremoval - find . -name '*.o' -exec rm -f {} ';' + find . -name '*.[oa]' -exec rm -f {} ';' find . -name '*.s[ol]' -exec rm -f {} ';' find $(srcdir)/build -name 'fficonfig.h' -exec rm -f {} ';' || true find $(srcdir)/build -name 'fficonfig.py' -exec rm -f {} ';' || true + find . -name '*.so.[0-9]*.[0-9]*' -exec rm -f {} ';' -rm -f Lib/lib2to3/*Grammar*.pickle profile-removal: Modified: python/branches/release26-maint/Misc/NEWS ============================================================================== --- python/branches/release26-maint/Misc/NEWS (original) +++ python/branches/release26-maint/Misc/NEWS Mon Aug 2 00:05:31 2010 @@ -89,8 +89,19 @@ Library ------- +- Issue #5146: Handle UID THREAD command correctly in imaplib. + +- Issue #5147: Fix the header generated for cookie files written by + http.cookiejar.MozillaCookieJar. + +- Issue #8198: In pydoc, output all help text to the correct stream + when sys.stdout is reassigned. + - Issue #1019882: Fix IndexError when loading certain hotshot stats. +- Issue #8471: In doctest, properly reset the output stream to an empty + string when Unicode was previously output. + - Issue #8397: Raise an error when attempting to mix iteration and regular reads on a BZ2File object, rather than returning incorrect results. From python-checkins at python.org Mon Aug 2 00:10:16 2010 From: python-checkins at python.org (georg.brandl) Date: Mon, 2 Aug 2010 00:10:16 +0200 (CEST) Subject: [Python-checkins] r83493 - in python/branches/release26-maint: Doc/library/ftplib.rst Doc/library/sys.rst Lib/bdb.py Lib/doctest.py Lib/ntpath.py Lib/pdb.py Lib/test/test_doctest.py Lib/test/test_ntpath.py Lib/test/test_optparse.py Misc/NEWS Python/getversion.c Message-ID: <20100801221016.0C62CEE9DD@mail.python.org> Author: georg.brandl Date: Mon Aug 2 00:10:15 2010 New Revision: 83493 Log: Merged revisions 83429,83436 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/release27-maint ................ r83429 | georg.brandl | 2010-08-01 21:14:56 +0200 (So, 01 Aug 2010) | 37 lines Merged revisions 83352,83356-83358,83362,83366,83368-83369 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83352 | georg.brandl | 2010-07-31 20:11:07 +0200 (Sa, 31 Jul 2010) | 1 line #9440: Remove borderline test case that fails based on unpredictable conditions such as compiler flags. ........ r83356 | georg.brandl | 2010-07-31 21:29:15 +0200 (Sa, 31 Jul 2010) | 1 line Remove trailing whitespace. ........ r83357 | georg.brandl | 2010-07-31 21:59:55 +0200 (Sa, 31 Jul 2010) | 1 line #5778: document that sys.version can contain a newline. ........ r83358 | georg.brandl | 2010-07-31 22:05:31 +0200 (Sa, 31 Jul 2010) | 1 line #9442: do not document a specific format for sys.version; rather refer to version_info and the platform module. ........ r83362 | georg.brandl | 2010-07-31 23:12:15 +0200 (Sa, 31 Jul 2010) | 1 line #8910: add a file explaining why Lib/test/data is there. ........ r83366 | georg.brandl | 2010-07-31 23:26:40 +0200 (Sa, 31 Jul 2010) | 1 line There always is a False and True now. ........ r83368 | georg.brandl | 2010-07-31 23:40:15 +0200 (Sa, 31 Jul 2010) | 1 line #7909: the prefixes \\.\ and \\?\ indicate special Windows paths, do not try to manipulate them. See http://msdn.microsoft.com/en-us/library/aa365247%28VS.85%29.aspx for details. ........ r83369 | georg.brandl | 2010-07-31 23:41:42 +0200 (Sa, 31 Jul 2010) | 1 line Fix "Berkeley" name. ........ ................ r83436 | georg.brandl | 2010-08-01 21:33:15 +0200 (So, 01 Aug 2010) | 42 lines Merged revisions 83259,83261,83264-83265,83268-83269,83271-83272,83281 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83259 | georg.brandl | 2010-07-30 09:03:39 +0200 (Fr, 30 Jul 2010) | 1 line Clarification. ........ r83261 | georg.brandl | 2010-07-30 09:21:26 +0200 (Fr, 30 Jul 2010) | 1 line #9230: allow Pdb.checkline() to be called without a current frame, for setting breakpoints before starting debugging. ........ r83264 | georg.brandl | 2010-07-30 10:45:26 +0200 (Fr, 30 Jul 2010) | 1 line Document the "jump" command in pdb.__doc__, and add a version tag for "until X". ........ r83265 | georg.brandl | 2010-07-30 10:54:49 +0200 (Fr, 30 Jul 2010) | 1 line #8015: fix crash when entering an empty line for breakpoint commands. Also restore environment properly when an exception occurs during the definition of commands. ........ r83268 | georg.brandl | 2010-07-30 11:23:23 +0200 (Fr, 30 Jul 2010) | 2 lines Issue #8048: Prevent doctests from failing when sys.displayhook has been reassigned. ........ r83269 | georg.brandl | 2010-07-30 11:43:00 +0200 (Fr, 30 Jul 2010) | 1 line #6719: In pdb, do not stop somewhere in the encodings machinery if the source file to be debugged is in a non-builtin encoding. ........ r83271 | georg.brandl | 2010-07-30 11:59:28 +0200 (Fr, 30 Jul 2010) | 1 line #5727: Restore the ability to use readline when calling into pdb in doctests. ........ r83272 | georg.brandl | 2010-07-30 12:29:19 +0200 (Fr, 30 Jul 2010) | 1 line #5294: Fix the behavior of pdb "continue" command when called in the top-level debugged frame. ........ r83281 | georg.brandl | 2010-07-30 15:36:43 +0200 (Fr, 30 Jul 2010) | 1 line Add myself for pdb. ........ ................ Modified: python/branches/release26-maint/ (props changed) python/branches/release26-maint/Doc/library/ftplib.rst python/branches/release26-maint/Doc/library/sys.rst python/branches/release26-maint/Lib/bdb.py python/branches/release26-maint/Lib/doctest.py python/branches/release26-maint/Lib/ntpath.py python/branches/release26-maint/Lib/pdb.py python/branches/release26-maint/Lib/test/test_doctest.py python/branches/release26-maint/Lib/test/test_ntpath.py python/branches/release26-maint/Lib/test/test_optparse.py python/branches/release26-maint/Misc/NEWS python/branches/release26-maint/Python/getversion.c Modified: python/branches/release26-maint/Doc/library/ftplib.rst ============================================================================== --- python/branches/release26-maint/Doc/library/ftplib.rst (original) +++ python/branches/release26-maint/Doc/library/ftplib.rst Mon Aug 2 00:10:15 2010 @@ -186,9 +186,9 @@ Retrieve a file or directory listing in ASCII transfer mode. *command* should be an appropriate ``RETR`` command (see :meth:`retrbinary`) or a command such as ``LIST``, ``NLST`` or ``MLSD`` (usually just the string - ``'LIST'``). The *callback* function is called for each line, with the - trailing CRLF stripped. The default *callback* prints the line to - ``sys.stdout``. + ``'LIST'``). The *callback* function is called for each line with a + string argument containing the line with the trailing CRLF stripped. + The default *callback* prints the line to ``sys.stdout``. .. method:: FTP.set_pasv(boolean) Modified: python/branches/release26-maint/Doc/library/sys.rst ============================================================================== --- python/branches/release26-maint/Doc/library/sys.rst (original) +++ python/branches/release26-maint/Doc/library/sys.rst Mon Aug 2 00:10:15 2010 @@ -909,14 +909,10 @@ .. data:: version A string containing the version number of the Python interpreter plus additional - information on the build number and compiler used. It has a value of the form - ``'version (#build_number, build_date, build_time) [compiler]'``. The first - three characters are used to identify the version in the installation - directories (where appropriate on each platform). An example:: - - >>> import sys - >>> sys.version - '1.5.2 (#0 Apr 13 1999, 10:51:12) [MSC 32 bit (Intel)]' + information on the build number and compiler used. This string is displayed + when the interactive interpreter is started. Do not extract version information + out of it, rather, use :data:`version_info` and the functions provided by the + :mod:`platform` module. .. data:: api_version Modified: python/branches/release26-maint/Lib/bdb.py ============================================================================== --- python/branches/release26-maint/Lib/bdb.py (original) +++ python/branches/release26-maint/Lib/bdb.py Mon Aug 2 00:10:15 2010 @@ -98,6 +98,8 @@ # (CT) stopframe may now also be None, see dispatch_call. # (CT) the former test for None is therefore removed from here. if frame is self.stopframe: + if self.stoplineno == -1: + return False return frame.f_lineno >= self.stoplineno while frame is not None and frame is not self.stopframe: if frame is self.botframe: @@ -155,10 +157,12 @@ but only if we are to stop at or just below this level.""" pass - def _set_stopinfo(self, stopframe, returnframe, stoplineno=-1): + def _set_stopinfo(self, stopframe, returnframe, stoplineno=0): self.stopframe = stopframe self.returnframe = returnframe self.quitting = 0 + # stoplineno >= 0 means: stop at line >= the stoplineno + # stoplineno -1 means: don't stop at all self.stoplineno = stoplineno # Derived classes and clients can call the following methods @@ -171,7 +175,7 @@ def set_step(self): """Stop after one line of code.""" - self._set_stopinfo(None,None) + self._set_stopinfo(None, None) def set_next(self, frame): """Stop on the next line in or below the given frame.""" @@ -198,7 +202,7 @@ def set_continue(self): # Don't stop except at breakpoints or when finished - self._set_stopinfo(self.botframe, None) + self._set_stopinfo(self.botframe, None, -1) if not self.breaks: # no breakpoints; run without debugger overhead sys.settrace(None) Modified: python/branches/release26-maint/Lib/doctest.py ============================================================================== --- python/branches/release26-maint/Lib/doctest.py (original) +++ python/branches/release26-maint/Lib/doctest.py Mon Aug 2 00:10:15 2010 @@ -335,6 +335,8 @@ self.__out = out self.__debugger_used = False pdb.Pdb.__init__(self, stdout=out) + # still use input() to get user input + self.use_rawinput = 1 def set_trace(self, frame=None): self.__debugger_used = True @@ -1381,12 +1383,17 @@ self.save_linecache_getlines = linecache.getlines linecache.getlines = self.__patched_linecache_getlines + # Make sure sys.displayhook just prints the value to stdout + save_displayhook = sys.displayhook + sys.displayhook = sys.__displayhook__ + try: return self.__run(test, compileflags, out) finally: sys.stdout = save_stdout pdb.set_trace = save_set_trace linecache.getlines = self.save_linecache_getlines + sys.displayhook = save_displayhook if clear_globs: test.globs.clear() Modified: python/branches/release26-maint/Lib/ntpath.py ============================================================================== --- python/branches/release26-maint/Lib/ntpath.py (original) +++ python/branches/release26-maint/Lib/ntpath.py Mon Aug 2 00:10:15 2010 @@ -399,6 +399,12 @@ """Normalize path, eliminating double slashes, etc.""" # Preserve unicode (if path is unicode) backslash, dot = (u'\\', u'.') if isinstance(path, unicode) else ('\\', '.') + if path.startswith(('\\\\.\\', '\\\\?\\')): + # in the case of paths with these prefixes: + # \\.\ -> device names + # \\?\ -> literal paths + # do not do any normalization, but return the path unchanged + return path path = path.replace("/", "\\") prefix, path = splitdrive(path) # We need to be careful here. If the prefix is empty, and the path starts Modified: python/branches/release26-maint/Lib/pdb.py ============================================================================== --- python/branches/release26-maint/Lib/pdb.py (original) +++ python/branches/release26-maint/Lib/pdb.py Mon Aug 2 00:10:15 2010 @@ -171,14 +171,18 @@ def user_return(self, frame, return_value): """This function is called when a return trap is set here.""" + if self._wait_for_mainpyfile: + return frame.f_locals['__return__'] = return_value print >>self.stdout, '--Return--' self.interaction(frame, None) def user_exception(self, frame, exc_info): - exc_type, exc_value, exc_traceback = exc_info """This function is called if an exception occurs, but only if we are to stop at or just below this level.""" + if self._wait_for_mainpyfile: + return + exc_type, exc_value, exc_traceback = exc_info frame.f_locals['__exception__'] = exc_type, exc_value if type(exc_type) == type(''): exc_type_name = exc_type @@ -265,8 +269,10 @@ return self.handle_command_def(line) def handle_command_def(self,line): - """ Handles one command line during command list definition. """ + """Handles one command line during command list definition.""" cmd, arg, line = self.parseline(line) + if not cmd: + return if cmd == 'silent': self.commands_silent[self.commands_bnum] = True return # continue to handle other cmd def in the cmd list @@ -274,7 +280,7 @@ self.cmdqueue = [] return 1 # end of cmd list cmdlist = self.commands[self.commands_bnum] - if (arg): + if arg: cmdlist.append(cmd+' '+arg) else: cmdlist.append(cmd) @@ -313,9 +319,11 @@ prompt_back = self.prompt self.prompt = '(com) ' self.commands_defining = True - self.cmdloop() - self.commands_defining = False - self.prompt = prompt_back + try: + self.cmdloop() + finally: + self.commands_defining = False + self.prompt = prompt_back def do_break(self, arg, temporary = 0): # break [ ([filename:]lineno | function) [, "condition"] ] @@ -451,7 +459,10 @@ Return `lineno` if it is, 0 if not (e.g. a docstring, comment, blank line or EOF). Warning: testing is not comprehensive. """ - line = linecache.getline(filename, lineno, self.curframe.f_globals) + # this method should be callable before starting debugging, so default + # to "no globals" if there is no current frame + globs = self.curframe.f_globals if hasattr(self, 'curframe') else None + line = linecache.getline(filename, lineno, globs) if not line: print >>self.stdout, 'End of file' return 0 @@ -1280,7 +1291,7 @@ # changed by the user from the command line. There is a "restart" command which # allows explicit specification of command line arguments. pdb = Pdb() - while 1: + while True: try: pdb._runscript(mainpyfile) if pdb._user_requested_quit: Modified: python/branches/release26-maint/Lib/test/test_doctest.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_doctest.py (original) +++ python/branches/release26-maint/Lib/test/test_doctest.py Mon Aug 2 00:10:15 2010 @@ -908,6 +908,35 @@ ZeroDivisionError: integer division or modulo by zero TestResults(failed=1, attempted=1) """ + def displayhook(): r""" +Test that changing sys.displayhook doesn't matter for doctest. + + >>> import sys + >>> orig_displayhook = sys.displayhook + >>> def my_displayhook(x): + ... print('hi!') + >>> sys.displayhook = my_displayhook + >>> def f(): + ... ''' + ... >>> 3 + ... 3 + ... ''' + >>> test = doctest.DocTestFinder().find(f)[0] + >>> r = doctest.DocTestRunner(verbose=False).run(test) + >>> post_displayhook = sys.displayhook + + We need to restore sys.displayhook now, so that we'll be able to test + results. + + >>> sys.displayhook = orig_displayhook + + Ok, now we can check that everything is ok. + + >>> r + TestResults(failed=0, attempted=1) + >>> post_displayhook is my_displayhook + True +""" def optionflags(): r""" Tests of `DocTestRunner`'s option flag handling. Modified: python/branches/release26-maint/Lib/test/test_ntpath.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_ntpath.py (original) +++ python/branches/release26-maint/Lib/test/test_ntpath.py Mon Aug 2 00:10:15 2010 @@ -128,6 +128,9 @@ self.assertTrue(isinstance(ntpath.normpath(path), unicode), 'normpath() returned str instead of unicode') + tester("ntpath.normpath('\\\\.\\NUL')", r'\\.\NUL') + tester("ntpath.normpath('\\\\?\\D:/XY\\Z')", r'\\?\D:/XY\Z') + def test_expandvars(self): oldenv = os.environ.copy() try: Modified: python/branches/release26-maint/Lib/test/test_optparse.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_optparse.py (original) +++ python/branches/release26-maint/Lib/test/test_optparse.py Mon Aug 2 00:10:15 2010 @@ -799,15 +799,13 @@ (options, args) = self.assertParseOK(["-q"], {'verbose': 0}, []) - if hasattr(__builtins__, 'False'): - self.failUnless(options.verbose is False) + self.assertTrue(options.verbose is False) def test_bool_true(self): (options, args) = self.assertParseOK(["-v"], {'verbose': 1}, []) - if hasattr(__builtins__, 'True'): - self.failUnless(options.verbose is True) + self.assertTrue(options.verbose is True) def test_bool_flicker_on_and_off(self): self.assertParseOK(["-qvq", "-q", "-v"], Modified: python/branches/release26-maint/Misc/NEWS ============================================================================== --- python/branches/release26-maint/Misc/NEWS (original) +++ python/branches/release26-maint/Misc/NEWS Mon Aug 2 00:10:15 2010 @@ -89,6 +89,24 @@ Library ------- +- Issue #5294: Fix the behavior of pdb's "continue" command when called + in the top-level debugged frame. + +- Issue #5727: Restore the ability to use readline when calling into pdb + in doctests. + +- Issue #6719: In pdb, do not stop somewhere in the encodings machinery + if the source file to be debugged is in a non-builtin encoding. + +- Issue #8048: Prevent doctests from failing when sys.displayhook has + been reassigned. + +- Issue #8015: In pdb, do not crash when an empty line is entered as + a breakpoint command. + +- Issue #7909: Do not touch paths with the special prefixes ``\\.\`` + or ``\\?\`` in ntpath.normpath(). + - Issue #5146: Handle UID THREAD command correctly in imaplib. - Issue #5147: Fix the header generated for cookie files written by Modified: python/branches/release26-maint/Python/getversion.c ============================================================================== --- python/branches/release26-maint/Python/getversion.c (original) +++ python/branches/release26-maint/Python/getversion.c Mon Aug 2 00:10:15 2010 @@ -9,7 +9,7 @@ Py_GetVersion(void) { static char version[250]; - PyOS_snprintf(version, sizeof(version), "%.80s (%.80s) %.80s", + PyOS_snprintf(version, sizeof(version), "%.80s (%.80s) %.80s", PY_VERSION, Py_GetBuildInfo(), Py_GetCompiler()); return version; } From python-checkins at python.org Mon Aug 2 00:10:57 2010 From: python-checkins at python.org (raymond.hettinger) Date: Mon, 2 Aug 2010 00:10:57 +0200 (CEST) Subject: [Python-checkins] r83494 - in python/branches/py3k: Lib/collections.py Misc/NEWS Message-ID: <20100801221057.40F52EE99F@mail.python.org> Author: raymond.hettinger Date: Mon Aug 2 00:10:57 2010 New Revision: 83494 Log: Update OrderedDict implementation to match that in Py2.7. Modified: python/branches/py3k/Lib/collections.py python/branches/py3k/Misc/NEWS Modified: python/branches/py3k/Lib/collections.py ============================================================================== --- python/branches/py3k/Lib/collections.py (original) +++ python/branches/py3k/Lib/collections.py Mon Aug 2 00:10:57 2010 @@ -11,16 +11,12 @@ from keyword import iskeyword as _iskeyword import sys as _sys import heapq as _heapq -from weakref import proxy as _proxy from itertools import repeat as _repeat, chain as _chain, starmap as _starmap ################################################################################ ### OrderedDict ################################################################################ -class _Link(object): - __slots__ = 'prev', 'next', 'key', '__weakref__' - class OrderedDict(dict, MutableMapping): 'Dictionary that remembers insertion order' # An inherited dict maps keys to values. @@ -31,9 +27,7 @@ # The internal self.__map dictionary maps keys to links in a doubly linked list. # The circular doubly linked list starts and ends with a sentinel element. # The sentinel element never gets deleted (this simplifies the algorithm). - # The prev/next links are weakref proxies (to prevent circular references). - # Individual links are kept alive by the hard reference in self.__map. - # Those hard references disappear when a key is deleted from an OrderedDict. + # Each link is stored as a list of length three: [PREV, NEXT, KEY]. def __init__(self, *args, **kwds): '''Initialize an ordered dictionary. Signature is the same as for @@ -46,56 +40,51 @@ try: self.__root except AttributeError: - self.__root = root = _Link() # sentinel node for the doubly linked list - root.prev = root.next = root + self.__root = root = [None, None, None] # sentinel node + PREV = 0 + NEXT = 1 + root[PREV] = root[NEXT] = root self.__map = {} self.update(*args, **kwds) - def clear(self): - 'od.clear() -> None. Remove all items from od.' - root = self.__root - root.prev = root.next = root - self.__map.clear() - dict.clear(self) - - def __setitem__(self, key, value): + def __setitem__(self, key, value, PREV=0, NEXT=1, dict_setitem=dict.__setitem__): 'od.__setitem__(i, y) <==> od[i]=y' # Setting a new item creates a new link which goes at the end of the linked # list, and the inherited dictionary is updated with the new key/value pair. if key not in self: - self.__map[key] = link = _Link() root = self.__root - last = root.prev - link.prev, link.next, link.key = last, root, key - last.next = root.prev = _proxy(link) - dict.__setitem__(self, key, value) + last = root[PREV] + last[NEXT] = root[PREV] = self.__map[key] = [last, root, key] + dict_setitem(self, key, value) - def __delitem__(self, key): + def __delitem__(self, key, PREV=0, NEXT=1, dict_delitem=dict.__delitem__): 'od.__delitem__(y) <==> del od[y]' # Deleting an existing item uses self.__map to find the link which is # then removed by updating the links in the predecessor and successor nodes. - dict.__delitem__(self, key) + dict_delitem(self, key) link = self.__map.pop(key) - link.prev.next = link.next - link.next.prev = link.prev + link_prev = link[PREV] + link_next = link[NEXT] + link_prev[NEXT] = link_next + link_next[PREV] = link_prev - def __iter__(self): + def __iter__(self, NEXT=1, KEY=2): 'od.__iter__() <==> iter(od)' # Traverse the linked list in order. root = self.__root - curr = root.next + curr = root[NEXT] while curr is not root: - yield curr.key - curr = curr.next + yield curr[KEY] + curr = curr[NEXT] - def __reversed__(self): + def __reversed__(self, PREV=0, KEY=2): 'od.__reversed__() <==> reversed(od)' # Traverse the linked list in reverse order. root = self.__root - curr = root.prev + curr = root[PREV] while curr is not root: - yield curr.key - curr = curr.prev + yield curr[KEY] + curr = curr[PREV] def __reduce__(self): 'Return state information for pickling' @@ -108,12 +97,24 @@ return (self.__class__, (items,), inst_dict) return self.__class__, (items,) + def clear(self): + 'od.clear() -> None. Remove all items from od.' + try: + for node in self.__map.values(): + del node[:] + self.__root[:] = [self.__root, self.__root, None] + self.__map.clear() + except AttributeError: + pass + dict.clear(self) + setdefault = MutableMapping.setdefault update = MutableMapping.update pop = MutableMapping.pop keys = MutableMapping.keys values = MutableMapping.values items = MutableMapping.items + __ne__ = MutableMapping.__ne__ def popitem(self, last=True): '''od.popitem() -> (k, v), return and remove a (key, value) pair. @@ -157,13 +158,8 @@ all(p==q for p, q in zip(self.items(), other.items())) return dict.__eq__(self, other) - def __ne__(self, other): - '''od.__ne__(y) <==> od!=y. Comparison to another OD is order-sensitive - while comparison to a regular mapping is order-insensitive. - - ''' - return not self == other - + def __del__(self): + self.clear() # eliminate cyclical references ################################################################################ Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Mon Aug 2 00:10:57 2010 @@ -29,6 +29,9 @@ Library ------- +- Update collections.OrderedDict to match the implementation in Py2.7 + (based on lists instead of weakly referenced Link objects). + - Issue #8397: Raise an error when attempting to mix iteration and regular reads on a BZ2File object, rather than returning incorrect results. From python-checkins at python.org Mon Aug 2 00:13:33 2010 From: python-checkins at python.org (georg.brandl) Date: Mon, 2 Aug 2010 00:13:33 +0200 (CEST) Subject: [Python-checkins] r83495 - in python/branches/release26-maint: Doc/library/optparse.rst Lib/Cookie.py Lib/mailbox.py Lib/posixpath.py Lib/pstats.py Lib/test/test_cookie.py Lib/test/test_set.py Lib/timeit.py Lib/trace.py Misc/NEWS Message-ID: <20100801221333.D7924EEAC8@mail.python.org> Author: georg.brandl Date: Mon Aug 2 00:13:33 2010 New Revision: 83495 Log: Merged revisions 83421,83424 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/release27-maint ................ r83421 | georg.brandl | 2010-08-01 20:52:52 +0200 (So, 01 Aug 2010) | 25 lines Merged revisions 83393,83396,83398,83405,83408 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83393 | georg.brandl | 2010-08-01 10:35:29 +0200 (So, 01 Aug 2010) | 1 line #1690103: fix initial namespace for code run with trace.main(). ........ r83396 | georg.brandl | 2010-08-01 10:52:32 +0200 (So, 01 Aug 2010) | 1 line #4810: document "--" option separator in timeit help. ........ r83398 | georg.brandl | 2010-08-01 11:06:34 +0200 (So, 01 Aug 2010) | 1 line #8826: the "expires" attribute value is a date string with spaces, but apparently not all user-agents put it in quotes. Handle that as a special case. ........ r83405 | georg.brandl | 2010-08-01 16:38:17 +0200 (So, 01 Aug 2010) | 1 line #4943: do not try to include drive letters (and colons) when looking for a probably module name. ........ r83408 | georg.brandl | 2010-08-01 17:30:56 +0200 (So, 01 Aug 2010) | 1 line #5551: symbolic links never can be mount points. Fixes the fix for #1713. ........ ................ r83424 | georg.brandl | 2010-08-01 21:02:09 +0200 (So, 01 Aug 2010) | 29 lines Merged revisions 83385-83389,83391 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83385 | georg.brandl | 2010-08-01 08:42:45 +0200 (So, 01 Aug 2010) | 1 line #8773: mailbox.py does not need to be executable. ........ r83386 | georg.brandl | 2010-08-01 08:44:46 +0200 (So, 01 Aug 2010) | 1 line #8768: name test method properly so that it gets executed. ........ r83387 | georg.brandl | 2010-08-01 08:53:28 +0200 (So, 01 Aug 2010) | 1 line #8735: better explain semantics of *values* argument for parse(). ........ r83388 | georg.brandl | 2010-08-01 09:48:43 +0200 (So, 01 Aug 2010) | 1 line #7395: fix traceback in do_add() when no stats are loaded. Apply same fix for do_sort() and do_reverse(). ........ r83389 | georg.brandl | 2010-08-01 09:57:47 +0200 (So, 01 Aug 2010) | 1 line Small improvements to pstats browser: do not crash on reading invalid file, and actually do a reload when executing "read" as intended. ........ r83391 | georg.brandl | 2010-08-01 10:10:08 +0200 (So, 01 Aug 2010) | 1 line Add another news entry. ........ ................ Modified: python/branches/release26-maint/ (props changed) python/branches/release26-maint/Doc/library/optparse.rst python/branches/release26-maint/Lib/Cookie.py python/branches/release26-maint/Lib/mailbox.py (props changed) python/branches/release26-maint/Lib/posixpath.py python/branches/release26-maint/Lib/pstats.py python/branches/release26-maint/Lib/test/test_cookie.py python/branches/release26-maint/Lib/test/test_set.py python/branches/release26-maint/Lib/timeit.py python/branches/release26-maint/Lib/trace.py python/branches/release26-maint/Misc/NEWS Modified: python/branches/release26-maint/Doc/library/optparse.rst ============================================================================== --- python/branches/release26-maint/Doc/library/optparse.rst (original) +++ python/branches/release26-maint/Doc/library/optparse.rst Mon Aug 2 00:13:33 2010 @@ -1233,8 +1233,9 @@ the list of arguments to process (default: ``sys.argv[1:]``) ``values`` - object to store option arguments in (default: a new instance of - :class:`optparse.Values`) + a :class:`optparse.Values` object to store option arguments in (default: a + new instance of :class:`Values`) -- if you give an existing object, the + option defaults will not be initialized on it and the return values are Modified: python/branches/release26-maint/Lib/Cookie.py ============================================================================== --- python/branches/release26-maint/Lib/Cookie.py (original) +++ python/branches/release26-maint/Lib/Cookie.py Mon Aug 2 00:13:33 2010 @@ -534,6 +534,8 @@ r"(?P" # Start of group 'val' r'"(?:[^\\"]|\\.)*"' # Any doublequoted string r"|" # or + r"\w{3},\s[\w\d-]{9,11}\s[\d:]{8}\sGMT" # Special case for "expires" attr + r"|" # or ""+ _LegalCharsPatt +"*" # Any word or empty string r")" # End of group 'val' r"\s*;?" # Probably ending in a semi-colon Modified: python/branches/release26-maint/Lib/posixpath.py ============================================================================== --- python/branches/release26-maint/Lib/posixpath.py (original) +++ python/branches/release26-maint/Lib/posixpath.py Mon Aug 2 00:13:33 2010 @@ -178,6 +178,9 @@ def ismount(path): """Test whether a path is a mount point""" + if islink(path): + # A symlink can never be a mount point + return False try: s1 = os.lstat(path) s2 = os.lstat(join(path, '..')) Modified: python/branches/release26-maint/Lib/pstats.py ============================================================================== --- python/branches/release26-maint/Lib/pstats.py (original) +++ python/branches/release26-maint/Lib/pstats.py Mon Aug 2 00:13:33 2010 @@ -597,7 +597,10 @@ print >> self.stream, " that match it are printed." def do_add(self, line): - self.stats.add(line) + if self.stats: + self.stats.add(line) + else: + print >> self.stream, "No statistics object is loaded." return 0 def help_add(self): print >> self.stream, "Add profile info from given file to current statistics object." @@ -632,22 +635,33 @@ except IOError, args: print >> self.stream, args[1] return + except Exception as err: + print >> self.stream, err.__class__.__name__ + ':', err + return self.prompt = line + "% " elif len(self.prompt) > 2: - line = self.prompt[-2:] + line = self.prompt[:-2] + self.do_read(line) else: print >> self.stream, "No statistics object is current -- cannot reload." return 0 def help_read(self): print >> self.stream, "Read in profile data from a specified file." + print >> self.stream, "Without argument, reload the current file." def do_reverse(self, line): - self.stats.reverse_order() + if self.stats: + self.stats.reverse_order() + else: + print >> self.stream, "No statistics object is loaded." return 0 def help_reverse(self): print >> self.stream, "Reverse the sort order of the profiling report." def do_sort(self, line): + if not self.stats: + print >> self.stream, "No statistics object is loaded." + return abbrevs = self.stats.get_sort_arg_defs() if line and not filter(lambda x,a=abbrevs: x not in a,line.split()): self.stats.sort_stats(*line.split()) @@ -669,11 +683,16 @@ self.generic_help() def do_strip(self, line): - self.stats.strip_dirs() - return 0 + if self.stats: + self.stats.strip_dirs() + else: + print >> self.stream, "No statistics object is loaded." def help_strip(self): print >> self.stream, "Strip leading path information from filenames in the report." + def help_help(self): + print >> self.stream, "Show help for a given command." + def postcmd(self, stop, line): if stop: return stop Modified: python/branches/release26-maint/Lib/test/test_cookie.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_cookie.py (original) +++ python/branches/release26-maint/Lib/test/test_cookie.py Mon Aug 2 00:13:33 2010 @@ -66,6 +66,16 @@ """) + # loading 'expires' + C = Cookie.SimpleCookie() + C.load('Customer="W"; expires=Wed, 01-Jan-2010 00:00:00 GMT') + self.assertEqual(C['Customer']['expires'], + 'Wed, 01-Jan-2010 00:00:00 GMT') + C = Cookie.SimpleCookie() + C.load('Customer="W"; expires=Wed, 01-Jan-98 00:00:00 GMT') + self.assertEqual(C['Customer']['expires'], + 'Wed, 01-Jan-98 00:00:00 GMT') + def test_quoted_meta(self): # Try cookie with quoted meta-data C = Cookie.SimpleCookie() Modified: python/branches/release26-maint/Lib/test/test_set.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_set.py (original) +++ python/branches/release26-maint/Lib/test/test_set.py Mon Aug 2 00:13:33 2010 @@ -751,7 +751,7 @@ result = self.set ^ self.set self.assertEqual(result, empty_set) - def checkempty_symmetric_difference(self): + def test_empty_symmetric_difference(self): result = self.set ^ empty_set self.assertEqual(result, self.set) Modified: python/branches/release26-maint/Lib/timeit.py ============================================================================== --- python/branches/release26-maint/Lib/timeit.py (original) +++ python/branches/release26-maint/Lib/timeit.py Mon Aug 2 00:13:33 2010 @@ -9,7 +9,7 @@ Library usage: see the Timer class. Command line usage: - python timeit.py [-n N] [-r N] [-s S] [-t] [-c] [-h] [statement] + python timeit.py [-n N] [-r N] [-s S] [-t] [-c] [-h] [--] [statement] Options: -n/--number N: how many times to execute 'statement' (default: see below) @@ -19,6 +19,7 @@ -c/--clock: use time.clock() (default on Windows) -v/--verbose: print raw timing results; repeat for more digits precision -h/--help: print this usage message and exit + --: separate options from statement, use when statement starts with - statement: statement to be timed (default 'pass') A multi-line statement may be given by specifying each line as a Modified: python/branches/release26-maint/Lib/trace.py ============================================================================== --- python/branches/release26-maint/Lib/trace.py (original) +++ python/branches/release26-maint/Lib/trace.py Mon Aug 2 00:13:33 2010 @@ -195,11 +195,13 @@ base = path[len(longest) + 1:] else: base = path + # the drive letter is never part of the module name + drive, base = os.path.splitdrive(base) base = base.replace(os.sep, ".") if os.altsep: base = base.replace(os.altsep, ".") filename, ext = os.path.splitext(base) - return filename + return filename.lstrip(".") class CoverageResults: def __init__(self, counts=None, calledfuncs=None, infile=None, @@ -798,7 +800,16 @@ ignoredirs=ignore_dirs, infile=counts_file, outfile=counts_file, timing=timing) try: - t.run('execfile(%r)' % (progname,)) + with open(progname) as fp: + code = compile(fp.read(), progname, 'exec') + # try to emulate __main__ namespace as much as possible + globs = { + '__file__': progname, + '__name__': '__main__', + '__package__': None, + '__cached__': None, + } + t.runctx(code, globs, globs) except IOError, err: _err_exit("Cannot run file %r because: %s" % (sys.argv[0], err)) except SystemExit: Modified: python/branches/release26-maint/Misc/NEWS ============================================================================== --- python/branches/release26-maint/Misc/NEWS (original) +++ python/branches/release26-maint/Misc/NEWS Mon Aug 2 00:13:33 2010 @@ -89,6 +89,15 @@ Library ------- +- Issue #7395: Fix tracebacks in pstats interactive browser. + +- Issue #1713: Fix os.path.ismount(), which returned true for symbolic links + across devices. + +- Issue #8826: Properly load old-style "expires" attribute in http.cookies. + +- Issue #1690103: Fix initial namespace for code run with trace.main(). + - Issue #5294: Fix the behavior of pdb's "continue" command when called in the top-level debugged frame. From python-checkins at python.org Mon Aug 2 00:19:17 2010 From: python-checkins at python.org (georg.brandl) Date: Mon, 2 Aug 2010 00:19:17 +0200 (CEST) Subject: [Python-checkins] r83496 - in python/branches/release26-maint: Doc/Makefile Doc/distutils/apiref.rst Doc/distutils/extending.rst Doc/glossary.rst Doc/library/datetime.rst Message-ID: <20100801221917.5F2F6EEB17@mail.python.org> Author: georg.brandl Date: Mon Aug 2 00:19:17 2010 New Revision: 83496 Log: Merged revisions 82793-82794,82807,82876,83432 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/release27-maint ................ r82793 | georg.brandl | 2010-07-11 10:56:18 +0200 (So, 11 Jul 2010) | 9 lines Merged revisions 82790 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r82790 | georg.brandl | 2010-07-11 10:36:20 +0200 (So, 11 Jul 2010) | 1 line #3214 followup: add link to ABC entry. ........ ................ r82794 | georg.brandl | 2010-07-11 10:57:05 +0200 (So, 11 Jul 2010) | 9 lines Merged revisions 82789 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r82789 | georg.brandl | 2010-07-11 10:33:16 +0200 (So, 11 Jul 2010) | 1 line Silence makeindex. ........ ................ r82807 | georg.brandl | 2010-07-11 12:29:37 +0200 (So, 11 Jul 2010) | 9 lines Merged revisions 82806 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r82806 | georg.brandl | 2010-07-11 12:22:44 +0200 (So, 11 Jul 2010) | 1 line #9223: link to Command class reference, and move Command interface docs nearer to class docs. ........ ................ r82876 | georg.brandl | 2010-07-14 10:55:55 +0200 (Mi, 14 Jul 2010) | 13 lines Merged revisions 82872,82874 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r82872 | georg.brandl | 2010-07-14 10:53:18 +0200 (Mi, 14 Jul 2010) | 1 line Remove XXX from text. ........ r82874 | georg.brandl | 2010-07-14 10:54:40 +0200 (Mi, 14 Jul 2010) | 1 line #9235: fix missing import of sys. ........ ................ r83432 | georg.brandl | 2010-08-01 21:21:26 +0200 (So, 01 Aug 2010) | 13 lines Merged revisions 83328,83341 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83328 | raymond.hettinger | 2010-07-31 12:14:41 +0200 (Sa, 31 Jul 2010) | 1 line Document how to change OrderedDict update order from first to last. ........ r83341 | georg.brandl | 2010-07-31 13:40:07 +0200 (Sa, 31 Jul 2010) | 1 line #9430: document timedelta str() and repr(). ........ ................ Modified: python/branches/release26-maint/ (props changed) python/branches/release26-maint/Doc/Makefile python/branches/release26-maint/Doc/distutils/apiref.rst python/branches/release26-maint/Doc/distutils/extending.rst python/branches/release26-maint/Doc/glossary.rst python/branches/release26-maint/Doc/library/datetime.rst Modified: python/branches/release26-maint/Doc/Makefile ============================================================================== --- python/branches/release26-maint/Doc/Makefile (original) +++ python/branches/release26-maint/Doc/Makefile Mon Aug 2 00:19:17 2010 @@ -143,6 +143,7 @@ # archive the A4 latex -rm -r build/latex make latex PAPER=a4 + -sed -i 's/makeindex/makeindex -q/' build/latex/Makefile (cd build/latex; make clean && make all-pdf && make FMT=pdf zip bz2) cp build/latex/docs-pdf.zip dist/python-$(DISTVERSION)-docs-pdf-a4.zip cp build/latex/docs-pdf.tar.bz2 dist/python-$(DISTVERSION)-docs-pdf-a4.tar.bz2 @@ -150,6 +151,7 @@ # archive the letter latex rm -r build/latex make latex PAPER=letter + -sed -i 's/makeindex/makeindex -q/' build/latex/Makefile (cd build/latex; make clean && make all-pdf && make FMT=pdf zip bz2) cp build/latex/docs-pdf.zip dist/python-$(DISTVERSION)-docs-pdf-letter.zip cp build/latex/docs-pdf.tar.bz2 dist/python-$(DISTVERSION)-docs-pdf-letter.tar.bz2 Modified: python/branches/release26-maint/Doc/distutils/apiref.rst ============================================================================== --- python/branches/release26-maint/Doc/distutils/apiref.rst (original) +++ python/branches/release26-maint/Doc/distutils/apiref.rst Mon Aug 2 00:19:17 2010 @@ -21,7 +21,7 @@ .. function:: setup(arguments) The basic do-everything function that does most everything you could ever ask - for from a Distutils method. See XXXXX + for from a Distutils method. The setup function takes a large number of arguments. These are laid out in the following table. Modified: python/branches/release26-maint/Doc/distutils/extending.rst ============================================================================== --- python/branches/release26-maint/Doc/distutils/extending.rst (original) +++ python/branches/release26-maint/Doc/distutils/extending.rst Mon Aug 2 00:19:17 2010 @@ -15,8 +15,8 @@ should be copied into packages in addition to :file:`.py` files as a convenience. -Most distutils command implementations are subclasses of the :class:`Command` -class from :mod:`distutils.cmd`. New commands may directly inherit from +Most distutils command implementations are subclasses of the +:class:`distutils.cmd.Command` class. New commands may directly inherit from :class:`Command`, while replacements often derive from :class:`Command` indirectly, directly subclassing the command they are replacing. Commands are required to derive from :class:`Command`. Modified: python/branches/release26-maint/Doc/glossary.rst ============================================================================== --- python/branches/release26-maint/Doc/glossary.rst (original) +++ python/branches/release26-maint/Doc/glossary.rst Mon Aug 2 00:19:17 2010 @@ -160,9 +160,9 @@ must be a duck.") By emphasizing interfaces rather than specific types, well-designed code improves its flexibility by allowing polymorphic substitution. Duck-typing avoids tests using :func:`type` or - :func:`isinstance`. (Note, however, that duck-typing can be complemented - with abstract base classes.) Instead, it typically employs :func:`hasattr` - tests or :term:`EAFP` programming. + :func:`isinstance`. (Note, however, that duck-typing can be complemented + with :term:`abstract base class`\ es.) Instead, it typically employs + :func:`hasattr` tests or :term:`EAFP` programming. EAFP Easier to ask for forgiveness than permission. This common Python coding Modified: python/branches/release26-maint/Doc/library/datetime.rst ============================================================================== --- python/branches/release26-maint/Doc/library/datetime.rst (original) +++ python/branches/release26-maint/Doc/library/datetime.rst Mon Aug 2 00:19:17 2010 @@ -235,6 +235,14 @@ | ``abs(t)`` | equivalent to +\ *t* when ``t.days >= 0``, and| | | to -*t* when ``t.days < 0``. (2) | +--------------------------------+-----------------------------------------------+ +| ``str(t)`` | Returns a string in the form | +| | ``[D day[s], ][H]H:MM:SS[.UUUUUU]``, where D | +| | is negative for negative ``t``. (5) | ++--------------------------------+-----------------------------------------------+ +| ``repr(t)`` | Returns a string in the form | +| | ``datetime.timedelta(D[, S[, U]])``, where D | +| | is negative for negative ``t``. (5) | ++--------------------------------+-----------------------------------------------+ Notes: @@ -250,6 +258,16 @@ (4) -*timedelta.max* is not representable as a :class:`timedelta` object. +(5) + String representations of :class:`timedelta` objects are normalized + similarly to their internal representation. This leads to somewhat + unusual results for negative timedeltas. For example: + + >>> timedelta(hours=-5) + datetime.timedelta(-1, 68400) + >>> print(_) + -1 day, 19:00:00 + In addition to the operations listed above :class:`timedelta` objects support certain additions and subtractions with :class:`date` and :class:`datetime` objects (see below). From python-checkins at python.org Mon Aug 2 00:19:52 2010 From: python-checkins at python.org (georg.brandl) Date: Mon, 2 Aug 2010 00:19:52 +0200 (CEST) Subject: [Python-checkins] r83497 - python/branches/release26-maint Message-ID: <20100801221952.9D751EEADE@mail.python.org> Author: georg.brandl Date: Mon Aug 2 00:19:52 2010 New Revision: 83497 Log: Blocked revisions 82588,82802-82803,82875 via svnmerge ................ r82588 | georg.brandl | 2010-07-05 19:50:55 +0200 (Mo, 05 Jul 2010) | 9 lines Merged revisions 82587 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r82587 | georg.brandl | 2010-07-05 19:48:38 +0200 (Mo, 05 Jul 2010) | 1 line #9166: NotImplemented is not an exception. ........ ................ r82802 | georg.brandl | 2010-07-11 11:34:16 +0200 (So, 11 Jul 2010) | 9 lines Merged revisions 82799 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r82799 | georg.brandl | 2010-07-11 11:26:57 +0200 (So, 11 Jul 2010) | 1 line Fix typo. ........ ................ r82803 | georg.brandl | 2010-07-11 11:36:31 +0200 (So, 11 Jul 2010) | 8 lines Blocked revisions 82801 via svnmerge ........ r82801 | georg.brandl | 2010-07-11 11:33:39 +0200 (So, 11 Jul 2010) | 1 line #9184: fix default value for "buffering" param of open(). ........ ................ r82875 | georg.brandl | 2010-07-14 10:55:03 +0200 (Mi, 14 Jul 2010) | 8 lines Blocked revisions 82873 via svnmerge ........ r82873 | georg.brandl | 2010-07-14 10:53:36 +0200 (Mi, 14 Jul 2010) | 1 line Remove unused code that would raise a NameError. ........ ................ Modified: python/branches/release26-maint/ (props changed) From python-checkins at python.org Mon Aug 2 00:26:02 2010 From: python-checkins at python.org (georg.brandl) Date: Mon, 2 Aug 2010 00:26:02 +0200 (CEST) Subject: [Python-checkins] r83498 - in python/branches/release26-maint: Doc/library/collections.rst Doc/library/csv.rst Doc/library/dis.rst Doc/library/os.rst Doc/library/stdtypes.rst Doc/library/test.rst Doc/library/threading.rst Misc/NEWS Message-ID: <20100801222602.8C877EE989@mail.python.org> Author: georg.brandl Date: Mon Aug 2 00:26:02 2010 New Revision: 83498 Log: Merged revisions 82571,82603,82845,82852,82953,82981,83013,83077,83082,83164,83251 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/release27-maint ................ r82571 | senthil.kumaran | 2010-07-05 13:44:34 +0200 (Mo, 05 Jul 2010) | 9 lines Merged revisions 82570 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r82570 | senthil.kumaran | 2010-07-05 17:11:42 +0530 (Mon, 05 Jul 2010) | 3 lines Fix: Issue9091 Minor documentation clarification. ........ ................ r82603 | senthil.kumaran | 2010-07-06 05:06:53 +0200 (Di, 06 Jul 2010) | 9 lines Merged revisions 82601 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r82601 | senthil.kumaran | 2010-07-06 07:38:36 +0530 (Tue, 06 Jul 2010) | 3 lines Change 'lowered' to 'lowercased' ........ ................ r82845 | antoine.pitrou | 2010-07-12 22:11:52 +0200 (Mo, 12 Jul 2010) | 9 lines Merged revisions 82842 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r82842 | antoine.pitrou | 2010-07-12 22:01:52 +0200 (lun., 12 juil. 2010) | 3 lines Fix definition of len() and indexing for memoryview objects (part of #7696). ........ ................ r82852 | jeroen.ruigrok | 2010-07-13 17:08:30 +0200 (Di, 13 Jul 2010) | 9 lines Merged revisions 82849 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r82849 | jeroen.ruigrok | 2010-07-13 16:47:01 +0200 (di, 13 jul 2010) | 2 lines Fix documentation typo: wprite() -> write(). ........ ................ r82953 | benjamin.peterson | 2010-07-18 16:26:34 +0200 (So, 18 Jul 2010) | 9 lines Merged revisions 82952 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r82952 | benjamin.peterson | 2010-07-18 09:23:36 -0500 (Sun, 18 Jul 2010) | 1 line use classmethod ........ ................ r82981 | antoine.pitrou | 2010-07-19 20:10:42 +0200 (Mo, 19 Jul 2010) | 3 lines Issue #9304: fix example in the 2.x memoryview documentation. ................ r83013 | brett.cannon | 2010-07-21 11:52:10 +0200 (Mi, 21 Jul 2010) | 2 lines Backport r82456. ................ r83077 | brett.cannon | 2010-07-23 14:07:27 +0200 (Fr, 23 Jul 2010) | 12 lines Merged revisions 83072 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83072 | brett.cannon | 2010-07-23 12:31:31 +0100 (Fri, 23 Jul 2010) | 5 lines Document the fact that the 'test' package is meant only for use by Python itself and not by others. Closes issue 9255. ........ ................ r83082 | brett.cannon | 2010-07-23 14:30:10 +0200 (Fr, 23 Jul 2010) | 12 lines Merged revisions 83080 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83080 | brett.cannon | 2010-07-23 13:26:35 +0100 (Fri, 23 Jul 2010) | 5 lines Clarify the wording for threading.is_alive() to not suggest something is "roughly" done. Closes issue 9339. Thanks Brian Brazil for the patch. ........ ................ r83164 | andrew.kuchling | 2010-07-26 15:42:35 +0200 (Mo, 26 Jul 2010) | 9 lines Merged revisions 83163 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83163 | andrew.kuchling | 2010-07-26 09:08:58 -0400 (Mon, 26 Jul 2010) | 1 line Reword paragraph ........ ................ r83251 | skip.montanaro | 2010-07-30 04:04:10 +0200 (Fr, 30 Jul 2010) | 2 lines Make sure all files are opened in binary mode. ................ Modified: python/branches/release26-maint/ (props changed) python/branches/release26-maint/Doc/library/collections.rst python/branches/release26-maint/Doc/library/csv.rst python/branches/release26-maint/Doc/library/dis.rst python/branches/release26-maint/Doc/library/os.rst python/branches/release26-maint/Doc/library/stdtypes.rst python/branches/release26-maint/Doc/library/test.rst python/branches/release26-maint/Doc/library/threading.rst python/branches/release26-maint/Misc/NEWS Modified: python/branches/release26-maint/Doc/library/collections.rst ============================================================================== --- python/branches/release26-maint/Doc/library/collections.rst (original) +++ python/branches/release26-maint/Doc/library/collections.rst Mon Aug 2 00:26:02 2010 @@ -576,7 +576,7 @@ three additional methods and one attribute. To prevent conflicts with field names, the method and attribute names start with an underscore. -.. method:: somenamedtuple._make(iterable) +.. classmethod:: somenamedtuple._make(iterable) Class method that makes a new instance from an existing sequence or iterable. Modified: python/branches/release26-maint/Doc/library/csv.rst ============================================================================== --- python/branches/release26-maint/Doc/library/csv.rst (original) +++ python/branches/release26-maint/Doc/library/csv.rst Mon Aug 2 00:26:02 2010 @@ -78,7 +78,7 @@ A short usage example:: >>> import csv - >>> spamReader = csv.reader(open('eggs.csv'), delimiter=' ', quotechar='|') + >>> spamReader = csv.reader(open('eggs.csv', 'rb'), delimiter=' ', quotechar='|') >>> for row in spamReader: ... print ', '.join(row) Spam, Spam, Spam, Spam, Spam, Baked Beans @@ -116,7 +116,7 @@ A short usage example:: >>> import csv - >>> spamWriter = csv.writer(open('eggs.csv', 'w'), delimiter=' ', + >>> spamWriter = csv.writer(open('eggs.csv', 'wb'), delimiter=' ', ... quotechar='|', quoting=csv.QUOTE_MINIMAL) >>> spamWriter.writerow(['Spam'] * 5 + ['Baked Beans']) >>> spamWriter.writerow(['Spam', 'Lovely Spam', 'Wonderful Spam']) @@ -234,7 +234,7 @@ An example for :class:`Sniffer` use:: - csvfile = open("example.csv") + csvfile = open("example.csv", "rb") dialect = csv.Sniffer().sniff(csvfile.read(1024)) csvfile.seek(0) reader = csv.reader(csvfile, dialect) Modified: python/branches/release26-maint/Doc/library/dis.rst ============================================================================== --- python/branches/release26-maint/Doc/library/dis.rst (original) +++ python/branches/release26-maint/Doc/library/dis.rst Mon Aug 2 00:26:02 2010 @@ -6,11 +6,17 @@ :synopsis: Disassembler for Python bytecode. -The :mod:`dis` module supports the analysis of Python :term:`bytecode` by disassembling -it. Since there is no Python assembler, this module defines the Python assembly -language. The Python bytecode which this module takes as an input is defined -in the file :file:`Include/opcode.h` and used by the compiler and the -interpreter. +The :mod:`dis` module supports the analysis of CPython :term:`bytecode` by +disassembling it. The CPython bytecode which this module takes as an +input is defined in the file :file:`Include/opcode.h` and used by the compiler +and the interpreter. + +.. impl-detail:: + + Bytecode is an implementation detail of the CPython interpreter! No + guarantees are made that bytecode will not be added, removed, or changed + between versions of Python. Use of this module should not be considered to + work across Python VMs or Python releases. Example: Given the function :func:`myfunc`:: Modified: python/branches/release26-maint/Doc/library/os.rst ============================================================================== --- python/branches/release26-maint/Doc/library/os.rst (original) +++ python/branches/release26-maint/Doc/library/os.rst Mon Aug 2 00:26:02 2010 @@ -705,7 +705,7 @@ This function is intended for low-level I/O. For normal usage, use the built-in function :func:`open`, which returns a "file object" with - :meth:`~file.read` and :meth:`~file.wprite` methods (and many more). To + :meth:`~file.read` and :meth:`~file.write` methods (and many more). To wrap a file descriptor in a "file object", use :func:`fdopen`. @@ -2035,8 +2035,9 @@ The :mod:`subprocess` module provides more powerful facilities for spawning new processes and retrieving their results; using that module is preferable to using - this function. Use the :mod:`subprocess` module. Check especially the - :ref:`subprocess-replacements` section. + this function. See the + :ref:`subprocess-replacements` section in the :mod:`subprocess` documentation + for some helpful recipes. Availability: Unix, Windows. Modified: python/branches/release26-maint/Doc/library/stdtypes.rst ============================================================================== --- python/branches/release26-maint/Doc/library/stdtypes.rst (original) +++ python/branches/release26-maint/Doc/library/stdtypes.rst Mon Aug 2 00:26:02 2010 @@ -799,7 +799,8 @@ .. method:: str.capitalize() - Return a copy of the string with only its first character capitalized. + Return a copy of the string with its first character capitalized and the + rest lowercased. For 8-bit strings, this method is locale-dependent. Modified: python/branches/release26-maint/Doc/library/test.rst ============================================================================== --- python/branches/release26-maint/Doc/library/test.rst (original) +++ python/branches/release26-maint/Doc/library/test.rst Mon Aug 2 00:26:02 2010 @@ -6,6 +6,13 @@ :synopsis: Regression tests package containing the testing suite for Python. .. sectionauthor:: Brett Cannon +.. note:: + The :mod:`test` package is meant for internal use by Python only. It is + documented for the benefit of the core developers of Python. Any use of + this package outside of Python's standard library is discouraged as code + mentioned here can change or be removed without notice between releases of + Python. + The :mod:`test` package contains all regression tests for Python as well as the modules :mod:`test.test_support` and :mod:`test.regrtest`. Modified: python/branches/release26-maint/Doc/library/threading.rst ============================================================================== --- python/branches/release26-maint/Doc/library/threading.rst (original) +++ python/branches/release26-maint/Doc/library/threading.rst Mon Aug 2 00:26:02 2010 @@ -319,8 +319,8 @@ Return whether the thread is alive. - Roughly, a thread is alive from the moment the :meth:`start` method - returns until its :meth:`run` method terminates. The module function + This method returns ``True`` just before the :meth:`run` method starts + until just after the :meth:`run` method terminates. The module function :func:`.enumerate` returns a list of all alive threads. .. method:: isDaemon() Modified: python/branches/release26-maint/Misc/NEWS ============================================================================== --- python/branches/release26-maint/Misc/NEWS (original) +++ python/branches/release26-maint/Misc/NEWS Mon Aug 2 00:26:02 2010 @@ -437,6 +437,9 @@ Documentation ------------- +- Issue #9255: Document that the 'test' package is for internal Python use + only. + - Issue #8909: Added the size of the bitmap used in the installer created by distutils' bdist_wininst. Patch by Anatoly Techtonik. From python-checkins at python.org Mon Aug 2 00:27:39 2010 From: python-checkins at python.org (georg.brandl) Date: Mon, 2 Aug 2010 00:27:39 +0200 (CEST) Subject: [Python-checkins] r83499 - python/branches/release27-maint/Misc/NEWS Message-ID: <20100801222739.9CB02EE989@mail.python.org> Author: georg.brandl Date: Mon Aug 2 00:27:39 2010 New Revision: 83499 Log: Fix style of referring to issues. Modified: python/branches/release27-maint/Misc/NEWS Modified: python/branches/release27-maint/Misc/NEWS ============================================================================== --- python/branches/release27-maint/Misc/NEWS (original) +++ python/branches/release27-maint/Misc/NEWS Mon Aug 2 00:27:39 2010 @@ -149,9 +149,12 @@ - Issue #9280: Make sharedinstall depend on sharedmods. -- Issue #9275: The OSX installer once again installs links to binaries in ``/usr/local/bin`` +- Issue #9275: The OSX installer once again installs links to binaries in + ``/usr/local/bin``. -- Issue #9392: A framework build on OSX will once again use a versioned name of the ``2to3`` tool, that is you can use ``2to3-2.7`` to select the Python 2.7 edition of 2to3. +- Issue #9392: A framework build on OSX will once again use a versioned name + of the ``2to3`` tool, that is you can use ``2to3-2.7`` to select the Python + 2.7 edition of 2to3. Tests ----- @@ -161,9 +164,10 @@ Documentation ------------- -- Issue 9255: Document that the 'test' package is for internal Python use only. +- Issue #9255: Document that the 'test' package is for internal Python use + only. -- Issue 7829: Document in dis that bytecode is an implementation detail. +- Issue #7829: Document in dis that bytecode is an implementation detail. What's New in Python 2.7? @@ -2311,7 +2315,7 @@ Makefile besides AR and make Distutils use it. Original patch by David Cournapeau. -- Issue 5955: aifc's close method did not close the file it wrapped, now it +- Issue #5955: aifc's close method did not close the file it wrapped, now it does. This also means getfp method now returns the real fp. - Issue #4875: On win32, ctypes.util.find_library does no longer return @@ -2920,10 +2924,10 @@ - Issue #6244: Allow detect_tkinter to look for Tcl/Tk 8.6. -- Issue 5390: Add uninstall icon independent of whether file extensions are +- Issue #5390: Add uninstall icon independent of whether file extensions are installed. -- Issue 5809: Specifying both --enable-framework and --enable-shared is an +- Issue #5809: Specifying both --enable-framework and --enable-shared is an error. Configure now explicity tells you about this. - Issue #3585: Add pkg-config support. It creates a python-2.7.pc file and a @@ -3350,7 +3354,7 @@ - Issue #3772: Fixed regression problem in StreamHandler.emit(). -- Issue 600362: Relocated parse_qs() and parse_qsl(), from the cgi module +- Issue #600362: Relocated parse_qs() and parse_qsl(), from the cgi module to the urlparse one. Added a PendingDeprecationWarning in the old module, it will be deprecated in the future. @@ -3362,7 +3366,7 @@ - Issue #3719: platform.architecture() fails if there are spaces in the path to the Python binary. -- Issue 3602: Moved test.test_support.catch_warning() to +- Issue #3602: Moved test.test_support.catch_warning() to warnings.catch_warnings() along with some API cleanup. Expanding the tests for catch_warnings() also led to an improvement in the raising of a DeprecationWarning related to warnings.warn_explicit(). @@ -3520,7 +3524,7 @@ - Issue #2222: Fixed reference leak when occurred os.rename() fails unicode conversion on 2nd parameter. (windows only) -- Issue 2464. urllib2 now supports a malformation in the URL received +- Issue #2464: urllib2 now supports a malformation in the URL received in a redirect. - Silence the DeprecationWarning raised when importing mimetools in @@ -3580,9 +3584,9 @@ - Issue #3449: Update decimal module to use most recent specification (v. 1.68) and tests (v. 2.58) from IBM. -- Issue 3437: Bug fix in robotparser parsing of Allow: lines. +- Issue #3437: Bug fix in robotparser parsing of Allow: lines. -- Issue 1592: Improve error reporting when operations are attempted +- Issue #1592: Improve error reporting when operations are attempted on a closed shelf. - Deprecate the "ast" parser function aliases. @@ -4535,7 +4539,7 @@ - Issue #2055: Convert test_fcntl to unittest. -- Issue 1960: Convert test_gdbm to unittest. +- Issue #1960: Convert test_gdbm to unittest. - GHOP 294: Convert test_contains, test_crypt, and test_select to unittest. @@ -4580,7 +4584,7 @@ - Patch #1759: Backport of PEP 3129 class decorators. - Issue #1881: An internal parser limit has been increased. Also see - issue 215555 for a discussion. + issue #215555 for a discussion. - Added the future_builtins module, which contains hex() and oct(). These are the PEP 3127 version of these functions, designed to be @@ -6092,7 +6096,7 @@ - Tools/scripts/reindent.py now creates the backup file using shutil.copy to preserve user/group and permissions. Added also a --nobackup option to not create the backup if the user is concerned - regarding this. Check issue 1050828 for more details. + regarding this. Check issue #1050828 for more details. - Tools/scripts/win_add2path.py was added. The simple script modifes the PATH environment var of the HKCU tree and adds the python bin From python-checkins at python.org Mon Aug 2 00:28:18 2010 From: python-checkins at python.org (georg.brandl) Date: Mon, 2 Aug 2010 00:28:18 +0200 (CEST) Subject: [Python-checkins] r83500 - python/branches/release26-maint Message-ID: <20100801222818.CAD16EE989@mail.python.org> Author: georg.brandl Date: Mon Aug 2 00:28:18 2010 New Revision: 83500 Log: Blocked revisions 82513,82609,82620 via svnmerge ........ r82513 | benjamin.peterson | 2010-07-03 20:24:52 +0200 (Sa, 03 Jul 2010) | 1 line fix date ........ r82609 | vinay.sajip | 2010-07-06 17:08:55 +0200 (Di, 06 Jul 2010) | 1 line Minor improvements to logging documentation. ........ r82620 | matthias.klose | 2010-07-07 13:03:50 +0200 (Mi, 07 Jul 2010) | 2 lines Remove whatsnew item about --with-computed-gotos. ........ Modified: python/branches/release26-maint/ (props changed) From python-checkins at python.org Mon Aug 2 00:31:06 2010 From: python-checkins at python.org (georg.brandl) Date: Mon, 2 Aug 2010 00:31:06 +0200 (CEST) Subject: [Python-checkins] r83501 - python/branches/py3k/Misc/NEWS Message-ID: <20100801223106.0D00AEE989@mail.python.org> Author: georg.brandl Date: Mon Aug 2 00:31:05 2010 New Revision: 83501 Log: Fix style of referring to issues. Modified: python/branches/py3k/Misc/NEWS Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Mon Aug 2 00:31:05 2010 @@ -739,7 +739,7 @@ - Charset.body_encode now correctly handles base64 encoding by encoding with the output_charset before calling base64mime.encode. Passes the tests from 2.x - issue 1368247. + issue #1368247. - Issue #8845: sqlite3 Connection objects now have a read-only in_transaction attribute that is True iff there are uncommitted changes. @@ -1355,7 +1355,7 @@ argument added to the TextTestRunner constructor allowing a different result class to be used without having to subclass. -- Issue 7588: ``unittest.TextTestResult.getDescription`` now includes the test +- Issue #7588: ``unittest.TextTestResult.getDescription`` now includes the test name in failure reports even if the test has a docstring. - Issue #3001: Add a C implementation of recursive locks which is used by @@ -1774,10 +1774,10 @@ - Issue #6244: Allow detect_tkinter to look for Tcl/Tk 8.6. -- Issue 4601: 'make install' did not set the appropriate permissions on +- Issue #4601: 'make install' did not set the appropriate permissions on directories. -- Issue 5390: Add uninstall icon independent of whether file extensions are +- Issue #5390: Add uninstall icon independent of whether file extensions are installed. - Issue #7541: When using ``python-config`` with a framework install the @@ -1792,7 +1792,7 @@ Documentation ------------ -- Issue 9255: Document that the 'test' package is meant for internal Python use +- Issue #9255: Document that the 'test' package is meant for internal Python use only. - A small WSGI server was added as Tools/scripts/serve.py, and is used to @@ -2168,7 +2168,7 @@ file is a binary. Patch by Brodie Rao, tests by Daniel Diniz. This fix corrects a pydoc regression. -- Issue 5955: aifc's close method did not close the file it wrapped, +- Issue #5955: aifc's close method did not close the file it wrapped, now it does. This also means getfp method now returns the real fp. Installation @@ -2201,7 +2201,7 @@ Tests ----- -- Issue 5442: Tests for importlib were not properly skipping case-sensitivity +- Issue #5442: Tests for importlib were not properly skipping case-sensitivity tests on darwin even when the OS was installed on a case-sensitive filesystem. Also fixed tests that should not be run when sys.dont_write_bytecode is true. @@ -2317,7 +2317,7 @@ conversions (including all float formatting operations) will be correctly rounded on these platforms. - See issue 1580 discussions for details of platforms for which + See issue #1580 discussions for details of platforms for which this change does not apply. - Issue #5759: float() didn't call __float__ on str subclasses. From python-checkins at python.org Mon Aug 2 00:31:28 2010 From: python-checkins at python.org (georg.brandl) Date: Mon, 2 Aug 2010 00:31:28 +0200 (CEST) Subject: [Python-checkins] r83502 - in python/branches/release26-maint: Misc/NEWS Message-ID: <20100801223128.87F41EE989@mail.python.org> Author: georg.brandl Date: Mon Aug 2 00:31:28 2010 New Revision: 83502 Log: Merged revisions 83499 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/release27-maint ........ r83499 | georg.brandl | 2010-08-02 00:27:39 +0200 (Mo, 02 Aug 2010) | 1 line Fix style of referring to issues. ........ Modified: python/branches/release26-maint/ (props changed) python/branches/release26-maint/Misc/NEWS Modified: python/branches/release26-maint/Misc/NEWS ============================================================================== --- python/branches/release26-maint/Misc/NEWS (original) +++ python/branches/release26-maint/Misc/NEWS Mon Aug 2 00:31:28 2010 @@ -1047,7 +1047,7 @@ - Issue #5971: StreamHandler.handleError now swallows IOErrors which occur when trying to print a traceback. -- Issue 5955: aifc's close method did not close the file it wrapped, +- Issue #5955: aifc's close method did not close the file it wrapped, now it does. This also means getfp method now returns the real fp. - Issue #4875: On win32, ctypes.util.find_library does no longer @@ -1157,7 +1157,7 @@ - Issue #6802: Fix build issues on MacOSX 10.6 -- Issue 5390: Add uninstall icon independent of whether file +- Issue #5390: Add uninstall icon independent of whether file extensions are installed. - Issue #6094: Build correctly with Subversion 1.7. @@ -1165,7 +1165,7 @@ - Issue #5726: Make Modules/ld_so_aix return the actual exit code of the linker, rather than always exit successfully. Patch by Floris Bruynooghe. -- Issue 5809: Specifying both --enable-framework and --enable-shared is +- Issue #5809: Specifying both --enable-framework and --enable-shared is an error. Configure now explicity tells you about this. Documentation @@ -1219,7 +1219,7 @@ - Issue #5013: Fixed a bug in FileHandler which occurred when the delay parameter was set. -- Issue 1242657: the __len__() and __length_hint__() calls in several tools +- Issue #1242657: the __len__() and __length_hint__() calls in several tools were suppressing all exceptions. These include list(), filter(), map(), zip(), and bytearray(). @@ -1299,9 +1299,9 @@ - Issue #1491431: Fixed distutils.filelist.glob_to_re for edge cases. Initial fix by Wayne Davison. -- Issue 5471: Fix os.path.expanduser() for $HOME set to '/'. +- Issue #5471: Fix os.path.expanduser() for $HOME set to '/'. -- Issue 1726172: fix IndexError in the case of and empty response in ftplib. +- Issue #1726172: fix IndexError in the case of and empty response in ftplib. - In Pdb, prevent the reassignment of __builtin__._ by sys.displayhook on printing out values. @@ -1316,7 +1316,7 @@ - Actually make the SimpleXMLRPCServer CGI handler work. -- Issue 2625: added missing iteritems() call to the for loop in +- Issue #2625: added missing iteritems() call to the for loop in mailbox.MH.get_message(). - Issue #5647: MutableSet.__iand__() no longer mutates self during iteration. @@ -1435,10 +1435,10 @@ inside its source path if it began with the same letters (e.g. "src" vs. "src.new"). -- Issue 4920: Fixed .next() vs .__next__() issues in the ABCs for +- Issue #4920: Fixed .next() vs .__next__() issues in the ABCs for Iterator and MutableSet. -- Issue 5021: doctest.testfile() did not create __name__ and +- Issue #5021: doctest.testfile() did not create __name__ and collections.namedtuple() relied on __name__ being defined. - Issue #3881: Help Tcl to load even when started through the @@ -1455,14 +1455,14 @@ - Issue #3997: zipfiles generated with more than 65536 files could not be opened with other applications. -- Issue 4816: itertools.combinations() and itertools.product were raising +- Issue #4816: itertools.combinations() and itertools.product were raising a ValueError for values of *r* larger than the input iterable. They now correctly return an empty iterator. - Fractions.from_float() no longer loses precision for integers too big to cast as floats. -- Issue 4790: The nsmallest() and nlargest() functions in the heapq module +- Issue #4790: The nsmallest() and nlargest() functions in the heapq module did unnecessary work in the common case where no key function was specified. - Issue #3767: Convert Tk object to string in tkColorChooser. @@ -1926,7 +1926,7 @@ - Issue #3772: Fixed regression problem in StreamHandler.emit(). -- Issue 600362: Relocated parse_qs() and parse_qsl(), from the cgi module +- Issue #600362: Relocated parse_qs() and parse_qsl(), from the cgi module to the urlparse one. Added a PendingDeprecationWarning in the old module, it will be deprecated in the future. @@ -1938,7 +1938,7 @@ - Issue #3719: platform.architecture() fails if there are spaces in the path to the Python binary. -- Issue 3602: Moved test.test_support.catch_warning() to +- Issue #3602: Moved test.test_support.catch_warning() to warnings.catch_warnings() along with some API cleanup. Expanding the tests for catch_warnings() also led to an improvement in the raising of a DeprecationWarning related to warnings.warn_explicit(). @@ -2093,7 +2093,7 @@ - Issue #2222: Fixed reference leak when occurred os.rename() fails unicode conversion on 2nd parameter. (windows only) -- Issue 2464. urllib2 now supports a malformation in the URL received +- Issue #2464: urllib2 now supports a malformation in the URL received in a redirect. - Silence the DeprecationWarning raised when importing mimetools in @@ -2153,9 +2153,9 @@ - Issue #3449: Update decimal module to use most recent specification (v. 1.68) and tests (v. 2.58) from IBM. -- Issue 3437: Bug fix in robotparser parsing of Allow: lines. +- Issue #3437: Bug fix in robotparser parsing of Allow: lines. -- Issue 1592: Improve error reporting when operations are attempted +- Issue #1592: Improve error reporting when operations are attempted on a closed shelf. - Deprecate the "ast" parser function aliases. @@ -3108,7 +3108,7 @@ - Issue #2055: Convert test_fcntl to unittest. -- Issue 1960: Convert test_gdbm to unittest. +- Issue #1960: Convert test_gdbm to unittest. - GHOP 294: Convert test_contains, test_crypt, and test_select to unittest. @@ -3153,7 +3153,7 @@ - Patch #1759: Backport of PEP 3129 class decorators. - Issue #1881: An internal parser limit has been increased. Also see - issue 215555 for a discussion. + issue #215555 for a discussion. - Added the future_builtins module, which contains hex() and oct(). These are the PEP 3127 version of these functions, designed to be @@ -4665,7 +4665,7 @@ - Tools/scripts/reindent.py now creates the backup file using shutil.copy to preserve user/group and permissions. Added also a --nobackup option to not create the backup if the user is concerned - regarding this. Check issue 1050828 for more details. + regarding this. Check issue #1050828 for more details. - Tools/scripts/win_add2path.py was added. The simple script modifes the PATH environment var of the HKCU tree and adds the python bin From python-checkins at python.org Mon Aug 2 00:33:13 2010 From: python-checkins at python.org (georg.brandl) Date: Mon, 2 Aug 2010 00:33:13 +0200 (CEST) Subject: [Python-checkins] r83503 - in python/branches/release31-maint: Misc/NEWS Message-ID: <20100801223313.12C73EE989@mail.python.org> Author: georg.brandl Date: Mon Aug 2 00:33:12 2010 New Revision: 83503 Log: Merged revisions 83501 via svnmerge from svn+ssh://svn.python.org/python/branches/py3k ........ r83501 | georg.brandl | 2010-08-02 00:31:05 +0200 (Mo, 02 Aug 2010) | 1 line Fix style of referring to issues. ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Misc/NEWS Modified: python/branches/release31-maint/Misc/NEWS ============================================================================== --- python/branches/release31-maint/Misc/NEWS (original) +++ python/branches/release31-maint/Misc/NEWS Mon Aug 2 00:33:12 2010 @@ -215,7 +215,7 @@ - Charset.body_encode now correctly handles base64 encoding by encoding with the output_charset before calling base64mime.encode. Passes the - tests from 2.x issue 1368247. + tests from 2.x issue #1368247. - Issue #7150: Raise OverflowError if the result of adding or subtracting timedelta from date or datetime falls outside of the MINYEAR:MAXYEAR range. @@ -446,7 +446,7 @@ Documentation ------------- -- Issue 9255: Document that the 'test' package is meant for interal Python use +- Issue #9255: Document that the 'test' package is meant for interal Python use only. - Issue #7829: Document in dis that bytecode is an implementation detail. @@ -1084,10 +1084,10 @@ Build ----- -- Issue 4601: 'make install' did not set the appropriate permissions on +- Issue #4601: 'make install' did not set the appropriate permissions on directories. -- Issue 5390: Add uninstall icon independent of whether file +- Issue #5390: Add uninstall icon independent of whether file extensions are installed. Test @@ -1309,7 +1309,7 @@ file is a binary. Patch by Brodie Rao, tests by Daniel Diniz. This fix corrects a pydoc regression. -- Issue 5955: aifc's close method did not close the file it wrapped, +- Issue #5955: aifc's close method did not close the file it wrapped, now it does. This also means getfp method now returns the real fp. Installation @@ -1342,7 +1342,7 @@ Tests ----- -- Issue 5442: Tests for importlib were not properly skipping case-sensitivity +- Issue #5442: Tests for importlib were not properly skipping case-sensitivity tests on darwin even when the OS was installed on a case-sensitive filesystem. Also fixed tests that should not be run when sys.dont_write_bytecode is true. @@ -1458,7 +1458,7 @@ conversions (including all float formatting operations) will be correctly rounded on these platforms. - See issue 1580 discussions for details of platforms for which + See issue #1580 discussions for details of platforms for which this change does not apply. - Issue #5759: float() didn't call __float__ on str subclasses. From python-checkins at python.org Mon Aug 2 00:43:54 2010 From: python-checkins at python.org (georg.brandl) Date: Mon, 2 Aug 2010 00:43:54 +0200 (CEST) Subject: [Python-checkins] r83504 - python/branches/release27-maint Message-ID: <20100801224354.CBF18EE992@mail.python.org> Author: georg.brandl Date: Mon Aug 2 00:43:54 2010 New Revision: 83504 Log: Blocked revisions 83501 via svnmerge ........ r83501 | georg.brandl | 2010-08-02 00:31:05 +0200 (Mo, 02 Aug 2010) | 1 line Fix style of referring to issues. ........ Modified: python/branches/release27-maint/ (props changed) From python-checkins at python.org Mon Aug 2 01:31:49 2010 From: python-checkins at python.org (ezio.melotti) Date: Mon, 2 Aug 2010 01:31:49 +0200 (CEST) Subject: [Python-checkins] r83505 - python/branches/release26-maint Message-ID: <20100801233149.0F8B8F4E2@mail.python.org> Author: ezio.melotti Date: Mon Aug 2 01:31:48 2010 New Revision: 83505 Log: Unblocked revisions 77310 via svnmerge ........ r77310 | antoine.pitrou | 2010-01-05 01:22:44 +0200 (Tue, 05 Jan 2010) | 4 lines Issue #7092: Fix the DeprecationWarnings emitted by the standard library when using the -3 flag. Patch by Florent Xicluna. ........ Modified: python/branches/release26-maint/ (props changed) From python-checkins at python.org Mon Aug 2 01:43:28 2010 From: python-checkins at python.org (r.david.murray) Date: Mon, 2 Aug 2010 01:43:28 +0200 (CEST) Subject: [Python-checkins] r83506 - python/branches/py3k/Misc/ACKS Message-ID: <20100801234328.3831BEEB4E@mail.python.org> Author: r.david.murray Date: Mon Aug 2 01:43:28 2010 New Revision: 83506 Log: Fix ACKS alphabetization. Modified: python/branches/py3k/Misc/ACKS Modified: python/branches/py3k/Misc/ACKS ============================================================================== --- python/branches/py3k/Misc/ACKS (original) +++ python/branches/py3k/Misc/ACKS Mon Aug 2 01:43:28 2010 @@ -195,9 +195,9 @@ Arnaud Delobelle Erik Demaine Roger Dev -Catherine Devlin Raghuram Devarakonda Caleb Deveraux +Catherine Devlin Toby Dickenson Mark Dickinson Jack Diederich From python-checkins at python.org Mon Aug 2 01:52:25 2010 From: python-checkins at python.org (r.david.murray) Date: Mon, 2 Aug 2010 01:52:25 +0200 (CEST) Subject: [Python-checkins] r83507 - in python/branches/release31-maint: Misc/ACKS Message-ID: <20100801235225.8AD24EE9F0@mail.python.org> Author: r.david.murray Date: Mon Aug 2 01:52:25 2010 New Revision: 83507 Log: Merged revisions 83506 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83506 | r.david.murray | 2010-08-01 19:43:28 -0400 (Sun, 01 Aug 2010) | 2 lines Fix ACKS alphabetization. ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Misc/ACKS Modified: python/branches/release31-maint/Misc/ACKS ============================================================================== --- python/branches/release31-maint/Misc/ACKS (original) +++ python/branches/release31-maint/Misc/ACKS Mon Aug 2 01:52:25 2010 @@ -179,8 +179,8 @@ Arnaud Delobelle Erik Demaine Roger Dev -Catherine Devlin Raghuram Devarakonda +Catherine Devlin Toby Dickenson Mark Dickinson Jack Diederich From python-checkins at python.org Mon Aug 2 01:52:42 2010 From: python-checkins at python.org (r.david.murray) Date: Mon, 2 Aug 2010 01:52:42 +0200 (CEST) Subject: [Python-checkins] r83508 - in python/branches/release27-maint: Misc/ACKS Message-ID: <20100801235242.25988EEB51@mail.python.org> Author: r.david.murray Date: Mon Aug 2 01:52:42 2010 New Revision: 83508 Log: Merged revisions 83506 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83506 | r.david.murray | 2010-08-01 19:43:28 -0400 (Sun, 01 Aug 2010) | 2 lines Fix ACKS alphabetization. ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Misc/ACKS Modified: python/branches/release27-maint/Misc/ACKS ============================================================================== --- python/branches/release27-maint/Misc/ACKS (original) +++ python/branches/release27-maint/Misc/ACKS Mon Aug 2 01:52:42 2010 @@ -188,9 +188,9 @@ Arnaud Delobelle Erik Demaine Roger Dev -Catherine Devlin Raghuram Devarakonda Scott Dial +Catherine Devlin Toby Dickenson Mark Dickinson Jack Diederich From python-checkins at python.org Mon Aug 2 01:53:40 2010 From: python-checkins at python.org (r.david.murray) Date: Mon, 2 Aug 2010 01:53:40 +0200 (CEST) Subject: [Python-checkins] r83509 - python/branches/release27-maint/Misc/ACKS Message-ID: <20100801235340.645F0EE9A4@mail.python.org> Author: r.david.murray Date: Mon Aug 2 01:53:40 2010 New Revision: 83509 Log: Fix the alphabetization fix (bad merge). Modified: python/branches/release27-maint/Misc/ACKS Modified: python/branches/release27-maint/Misc/ACKS ============================================================================== --- python/branches/release27-maint/Misc/ACKS (original) +++ python/branches/release27-maint/Misc/ACKS Mon Aug 2 01:53:40 2010 @@ -189,8 +189,8 @@ Erik Demaine Roger Dev Raghuram Devarakonda -Scott Dial Catherine Devlin +Scott Dial Toby Dickenson Mark Dickinson Jack Diederich From python-checkins at python.org Mon Aug 2 01:57:22 2010 From: python-checkins at python.org (r.david.murray) Date: Mon, 2 Aug 2010 01:57:22 +0200 (CEST) Subject: [Python-checkins] r83510 - in python/branches/release26-maint: Misc/ACKS Message-ID: <20100801235722.93243EEB70@mail.python.org> Author: r.david.murray Date: Mon Aug 2 01:57:22 2010 New Revision: 83510 Log: Merged revisions 83508-83509 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/release27-maint ................ r83508 | r.david.murray | 2010-08-01 19:52:42 -0400 (Sun, 01 Aug 2010) | 9 lines Merged revisions 83506 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83506 | r.david.murray | 2010-08-01 19:43:28 -0400 (Sun, 01 Aug 2010) | 2 lines Fix ACKS alphabetization. ........ ................ r83509 | r.david.murray | 2010-08-01 19:53:40 -0400 (Sun, 01 Aug 2010) | 2 lines Fix the alphabetization fix (bad merge). ................ Modified: python/branches/release26-maint/ (props changed) python/branches/release26-maint/Misc/ACKS Modified: python/branches/release26-maint/Misc/ACKS ============================================================================== --- python/branches/release26-maint/Misc/ACKS (original) +++ python/branches/release26-maint/Misc/ACKS Mon Aug 2 01:57:22 2010 @@ -170,8 +170,8 @@ Arnaud Delobelle Erik Demaine Roger Dev -Catherine Devlin Raghuram Devarakonda +Catherine Devlin Scott Dial Toby Dickenson Mark Dickinson From python-checkins at python.org Mon Aug 2 02:06:15 2010 From: python-checkins at python.org (ezio.melotti) Date: Mon, 2 Aug 2010 02:06:15 +0200 (CEST) Subject: [Python-checkins] r83511 - python/branches/release26-maint Message-ID: <20100802000615.8EE0DEEB8F@mail.python.org> Author: ezio.melotti Date: Mon Aug 2 02:06:15 2010 New Revision: 83511 Log: Unblocked revisions 77311 via svnmerge ........ r77311 | antoine.pitrou | 2010-01-05 01:28:16 +0200 (Tue, 05 Jan 2010) | 3 lines Kill a couple of "<>" ........ Modified: python/branches/release26-maint/ (props changed) From python-checkins at python.org Mon Aug 2 02:24:27 2010 From: python-checkins at python.org (ezio.melotti) Date: Mon, 2 Aug 2010 02:24:27 +0200 (CEST) Subject: [Python-checkins] r83512 - in python/branches/release26-maint: Lib/binhex.py Lib/bsddb/__init__.py Lib/compiler/ast.py Lib/compiler/pycodegen.py Lib/dbhash.py Lib/email/test/test_email.py Lib/email/test/test_email_renamed.py Lib/formatter.py Lib/imputil.py Lib/mailbox.py Lib/pprint.py Lib/pstats.py Lib/sets.py Lib/sunau.py Lib/test/test_wsgiref.py Lib/wave.py Lib/webbrowser.py Misc/NEWS Tools/compiler/astgen.py Message-ID: <20100802002427.5E23CEEBAF@mail.python.org> Author: ezio.melotti Date: Mon Aug 2 02:24:26 2010 New Revision: 83512 Log: Merged revisions 77310-77311 via svnmerge from svn+ssh://pythondev at svn.python.org/python/trunk ........ r77310 | antoine.pitrou | 2010-01-05 01:22:44 +0200 (Tue, 05 Jan 2010) | 4 lines Issue #7092: Fix the DeprecationWarnings emitted by the standard library when using the -3 flag. Patch by Florent Xicluna. ........ r77311 | antoine.pitrou | 2010-01-05 01:28:16 +0200 (Tue, 05 Jan 2010) | 3 lines Kill a couple of "<>" ........ Modified: python/branches/release26-maint/ (props changed) python/branches/release26-maint/Lib/binhex.py python/branches/release26-maint/Lib/bsddb/__init__.py python/branches/release26-maint/Lib/compiler/ast.py python/branches/release26-maint/Lib/compiler/pycodegen.py python/branches/release26-maint/Lib/dbhash.py python/branches/release26-maint/Lib/email/test/test_email.py python/branches/release26-maint/Lib/email/test/test_email_renamed.py python/branches/release26-maint/Lib/formatter.py python/branches/release26-maint/Lib/imputil.py python/branches/release26-maint/Lib/mailbox.py python/branches/release26-maint/Lib/pprint.py python/branches/release26-maint/Lib/pstats.py python/branches/release26-maint/Lib/sets.py python/branches/release26-maint/Lib/sunau.py python/branches/release26-maint/Lib/test/test_wsgiref.py python/branches/release26-maint/Lib/wave.py python/branches/release26-maint/Lib/webbrowser.py python/branches/release26-maint/Misc/NEWS python/branches/release26-maint/Tools/compiler/astgen.py Modified: python/branches/release26-maint/Lib/binhex.py ============================================================================== --- python/branches/release26-maint/Lib/binhex.py (original) +++ python/branches/release26-maint/Lib/binhex.py Mon Aug 2 02:24:26 2010 @@ -170,7 +170,8 @@ del self.ofp class BinHex: - def __init__(self, (name, finfo, dlen, rlen), ofp): + def __init__(self, name_finfo_dlen_rlen, ofp): + name, finfo, dlen, rlen = name_finfo_dlen_rlen if type(ofp) == type(''): ofname = ofp ofp = open(ofname, 'w') Modified: python/branches/release26-maint/Lib/bsddb/__init__.py ============================================================================== --- python/branches/release26-maint/Lib/bsddb/__init__.py (original) +++ python/branches/release26-maint/Lib/bsddb/__init__.py Mon Aug 2 02:24:26 2010 @@ -44,7 +44,7 @@ if sys.py3kwarning: import warnings - warnings.warnpy3k("in 3.x, bsddb has been removed; " + warnings.warnpy3k("in 3.x, the bsddb module has been removed; " "please use the pybsddb project instead", DeprecationWarning, 2) Modified: python/branches/release26-maint/Lib/compiler/ast.py ============================================================================== --- python/branches/release26-maint/Lib/compiler/ast.py (original) +++ python/branches/release26-maint/Lib/compiler/ast.py Mon Aug 2 02:24:26 2010 @@ -51,9 +51,9 @@ return "Expression(%s)" % (repr(self.node)) class Add(Node): - def __init__(self, (left, right), lineno=None): - self.left = left - self.right = right + def __init__(self, leftright, lineno=None): + self.left = leftright[0] + self.right = leftright[1] self.lineno = lineno def getChildren(self): @@ -431,9 +431,9 @@ return "Discard(%s)" % (repr(self.expr),) class Div(Node): - def __init__(self, (left, right), lineno=None): - self.left = left - self.right = right + def __init__(self, leftright, lineno=None): + self.left = leftright[0] + self.right = leftright[1] self.lineno = lineno def getChildren(self): @@ -485,9 +485,9 @@ return "Exec(%s, %s, %s)" % (repr(self.expr), repr(self.locals), repr(self.globals)) class FloorDiv(Node): - def __init__(self, (left, right), lineno=None): - self.left = left - self.right = right + def __init__(self, leftright, lineno=None): + self.left = leftright[0] + self.right = leftright[1] self.lineno = lineno def getChildren(self): @@ -560,7 +560,6 @@ self.kwargs = 1 - def getChildren(self): children = [] children.append(self.decorators) @@ -590,6 +589,7 @@ self.argnames = ['.0'] self.varargs = self.kwargs = None + def getChildren(self): return self.code, @@ -607,7 +607,6 @@ self.lineno = lineno self.is_outmost = False - def getChildren(self): children = [] children.append(self.assign) @@ -784,7 +783,6 @@ self.kwargs = 1 - def getChildren(self): children = [] children.append(self.argnames) @@ -803,9 +801,9 @@ return "Lambda(%s, %s, %s, %s)" % (repr(self.argnames), repr(self.defaults), repr(self.flags), repr(self.code)) class LeftShift(Node): - def __init__(self, (left, right), lineno=None): - self.left = left - self.right = right + def __init__(self, leftright, lineno=None): + self.left = leftright[0] + self.right = leftright[1] self.lineno = lineno def getChildren(self): @@ -893,9 +891,9 @@ return "ListCompIf(%s)" % (repr(self.test),) class Mod(Node): - def __init__(self, (left, right), lineno=None): - self.left = left - self.right = right + def __init__(self, leftright, lineno=None): + self.left = leftright[0] + self.right = leftright[1] self.lineno = lineno def getChildren(self): @@ -923,9 +921,9 @@ return "Module(%s, %s)" % (repr(self.doc), repr(self.node)) class Mul(Node): - def __init__(self, (left, right), lineno=None): - self.left = left - self.right = right + def __init__(self, leftright, lineno=None): + self.left = leftright[0] + self.right = leftright[1] self.lineno = lineno def getChildren(self): @@ -995,9 +993,9 @@ return "Pass()" class Power(Node): - def __init__(self, (left, right), lineno=None): - self.left = left - self.right = right + def __init__(self, leftright, lineno=None): + self.left = leftright[0] + self.right = leftright[1] self.lineno = lineno def getChildren(self): @@ -1095,9 +1093,9 @@ return "Return(%s)" % (repr(self.value),) class RightShift(Node): - def __init__(self, (left, right), lineno=None): - self.left = left - self.right = right + def __init__(self, leftright, lineno=None): + self.left = leftright[0] + self.right = leftright[1] self.lineno = lineno def getChildren(self): @@ -1170,9 +1168,9 @@ return "Stmt(%s)" % (repr(self.nodes),) class Sub(Node): - def __init__(self, (left, right), lineno=None): - self.left = left - self.right = right + def __init__(self, leftright, lineno=None): + self.left = leftright[0] + self.right = leftright[1] self.lineno = lineno def getChildren(self): Modified: python/branches/release26-maint/Lib/compiler/pycodegen.py ============================================================================== --- python/branches/release26-maint/Lib/compiler/pycodegen.py (original) +++ python/branches/release26-maint/Lib/compiler/pycodegen.py Mon Aug 2 02:24:26 2010 @@ -900,10 +900,10 @@ level = node.level if level == 0 and not self.graph.checkFlag(CO_FUTURE_ABSIMPORT): level = -1 - fromlist = map(lambda (name, alias): name, node.names) + fromlist = tuple(name for (name, alias) in node.names) if VERSION > 1: self.emit('LOAD_CONST', level) - self.emit('LOAD_CONST', tuple(fromlist)) + self.emit('LOAD_CONST', fromlist) self.emit('IMPORT_NAME', node.modname) for name, alias in node.names: if VERSION > 1: Modified: python/branches/release26-maint/Lib/dbhash.py ============================================================================== --- python/branches/release26-maint/Lib/dbhash.py (original) +++ python/branches/release26-maint/Lib/dbhash.py Mon Aug 2 02:24:26 2010 @@ -3,7 +3,7 @@ import sys if sys.py3kwarning: import warnings - warnings.warnpy3k("in 3.x, dbhash has been removed", DeprecationWarning, 2) + warnings.warnpy3k("in 3.x, the dbhash module has been removed", DeprecationWarning, 2) try: import bsddb except ImportError: Modified: python/branches/release26-maint/Lib/email/test/test_email.py ============================================================================== --- python/branches/release26-maint/Lib/email/test/test_email.py (original) +++ python/branches/release26-maint/Lib/email/test/test_email.py Mon Aug 2 02:24:26 2010 @@ -51,7 +51,7 @@ class TestEmailBase(unittest.TestCase): def ndiffAssertEqual(self, first, second): """Like failUnlessEqual except use ndiff for readable output.""" - if first <> second: + if first != second: sfirst = str(first) ssecond = str(second) diff = difflib.ndiff(sfirst.splitlines(), ssecond.splitlines()) @@ -2857,7 +2857,7 @@ # Try a charset with None body encoding c = Charset('us-ascii') eq('hello world', c.body_encode('hello world')) - # Try the convert argument, where input codec <> output codec + # Try the convert argument, where input codec != output codec c = Charset('euc-jp') # With apologies to Tokio Kikuchi ;) try: Modified: python/branches/release26-maint/Lib/email/test/test_email_renamed.py ============================================================================== --- python/branches/release26-maint/Lib/email/test/test_email_renamed.py (original) +++ python/branches/release26-maint/Lib/email/test/test_email_renamed.py Mon Aug 2 02:24:26 2010 @@ -52,7 +52,7 @@ class TestEmailBase(unittest.TestCase): def ndiffAssertEqual(self, first, second): """Like failUnlessEqual except use ndiff for readable output.""" - if first <> second: + if first != second: sfirst = str(first) ssecond = str(second) diff = difflib.ndiff(sfirst.splitlines(), ssecond.splitlines()) @@ -2761,7 +2761,7 @@ # Try a charset with None body encoding c = Charset('us-ascii') eq('hello world', c.body_encode('hello world')) - # Try the convert argument, where input codec <> output codec + # Try the convert argument, where input codec != output codec c = Charset('euc-jp') # With apologies to Tokio Kikuchi ;) try: Modified: python/branches/release26-maint/Lib/formatter.py ============================================================================== --- python/branches/release26-maint/Lib/formatter.py (original) +++ python/branches/release26-maint/Lib/formatter.py Mon Aug 2 02:24:26 2010 @@ -228,7 +228,8 @@ self.align = None self.writer.new_alignment(None) - def push_font(self, (size, i, b, tt)): + def push_font(self, font): + size, i, b, tt = font if self.softspace: self.hard_break = self.para_end = self.softspace = 0 self.nospace = 1 Modified: python/branches/release26-maint/Lib/imputil.py ============================================================================== --- python/branches/release26-maint/Lib/imputil.py (original) +++ python/branches/release26-maint/Lib/imputil.py Mon Aug 2 02:24:26 2010 @@ -281,7 +281,8 @@ setattr(parent, modname, module) return module - def _process_result(self, (ispkg, code, values), fqname): + def _process_result(self, result, fqname): + ispkg, code, values = result # did get_code() return an actual module? (rather than a code object) is_module = isinstance(code, _ModuleType) Modified: python/branches/release26-maint/Lib/mailbox.py ============================================================================== --- python/branches/release26-maint/Lib/mailbox.py (original) +++ python/branches/release26-maint/Lib/mailbox.py Mon Aug 2 02:24:26 2010 @@ -18,7 +18,6 @@ import email import email.message import email.generator -import rfc822 import StringIO try: if sys.platform == 'os2emx': @@ -28,6 +27,13 @@ except ImportError: fcntl = None +import warnings +with warnings.catch_warnings(): + if sys.py3kwarning: + warnings.filterwarnings("ignore", ".*rfc822 has been removed", + DeprecationWarning) + import rfc822 + __all__ = [ 'Mailbox', 'Maildir', 'mbox', 'MH', 'Babyl', 'MMDF', 'Message', 'MaildirMessage', 'mboxMessage', 'MHMessage', 'BabylMessage', 'MMDFMessage', 'UnixMailbox', Modified: python/branches/release26-maint/Lib/pprint.py ============================================================================== --- python/branches/release26-maint/Lib/pprint.py (original) +++ python/branches/release26-maint/Lib/pprint.py Mon Aug 2 02:24:26 2010 @@ -35,6 +35,7 @@ """ import sys as _sys +import warnings from cStringIO import StringIO as _StringIO @@ -70,6 +71,13 @@ """Determine if object requires a recursive representation.""" return _safe_repr(object, {}, None, 0)[2] +def _sorted(iterable): + with warnings.catch_warnings(): + if _sys.py3kwarning: + warnings.filterwarnings("ignore", "comparing unequal types " + "not supported", DeprecationWarning) + return sorted(iterable) + class PrettyPrinter: def __init__(self, indent=1, width=80, depth=None, stream=None): """Handle pretty printing operations onto a stream using a set of @@ -144,8 +152,7 @@ if length: context[objid] = 1 indent = indent + self._indent_per_level - items = object.items() - items.sort() + items = _sorted(object.items()) key, ent = items[0] rep = self._repr(key, context, level) write(rep) @@ -181,7 +188,7 @@ return write('set([') endchar = '])' - object = sorted(object) + object = _sorted(object) indent += 4 elif issubclass(typ, frozenset): if not length: @@ -189,7 +196,7 @@ return write('frozenset([') endchar = '])' - object = sorted(object) + object = _sorted(object) indent += 10 else: write('(') @@ -274,7 +281,7 @@ append = components.append level += 1 saferepr = _safe_repr - for k, v in sorted(object.items()): + 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)) Modified: python/branches/release26-maint/Lib/pstats.py ============================================================================== --- python/branches/release26-maint/Lib/pstats.py (original) +++ python/branches/release26-maint/Lib/pstats.py Mon Aug 2 02:24:26 2010 @@ -442,12 +442,12 @@ if nc == 0: print >> self.stream, ' '*8, else: - print >> self.stream, f8(tt/nc), + print >> self.stream, f8(float(tt)/nc), print >> self.stream, f8(ct), if cc == 0: print >> self.stream, ' '*8, else: - print >> self.stream, f8(ct/cc), + print >> self.stream, f8(float(ct)/cc), print >> self.stream, func_std_string(func) class TupleComp: Modified: python/branches/release26-maint/Lib/sets.py ============================================================================== --- python/branches/release26-maint/Lib/sets.py (original) +++ python/branches/release26-maint/Lib/sets.py Mon Aug 2 02:24:26 2010 @@ -54,29 +54,7 @@ # - Raymond Hettinger added a number of speedups and other # improvements. -from __future__ import generators -try: - from itertools import ifilter, ifilterfalse -except ImportError: - # Code to make the module run under Py2.2 - def ifilter(predicate, iterable): - if predicate is None: - def predicate(x): - return x - for x in iterable: - if predicate(x): - yield x - def ifilterfalse(predicate, iterable): - if predicate is None: - def predicate(x): - return x - for x in iterable: - if not predicate(x): - yield x - try: - True, False - except NameError: - True, False = (0==0, 0!=0) +from itertools import ifilter, ifilterfalse __all__ = ['BaseSet', 'Set', 'ImmutableSet'] @@ -235,7 +213,7 @@ little, big = self, other else: little, big = other, self - common = ifilter(big._data.has_key, little) + common = ifilter(big._data.__contains__, little) return self.__class__(common) def __xor__(self, other): @@ -260,9 +238,9 @@ otherdata = other._data except AttributeError: otherdata = Set(other)._data - for elt in ifilterfalse(otherdata.has_key, selfdata): + for elt in ifilterfalse(otherdata.__contains__, selfdata): data[elt] = value - for elt in ifilterfalse(selfdata.has_key, otherdata): + for elt in ifilterfalse(selfdata.__contains__, otherdata): data[elt] = value return result @@ -287,7 +265,7 @@ except AttributeError: otherdata = Set(other)._data value = True - for elt in ifilterfalse(otherdata.has_key, self): + for elt in ifilterfalse(otherdata.__contains__, self): data[elt] = value return result @@ -313,7 +291,7 @@ self._binary_sanity_check(other) if len(self) > len(other): # Fast check for obvious cases return False - for elt in ifilterfalse(other._data.has_key, self): + for elt in ifilterfalse(other._data.__contains__, self): return False return True @@ -322,7 +300,7 @@ self._binary_sanity_check(other) if len(self) < len(other): # Fast check for obvious cases return False - for elt in ifilterfalse(self._data.has_key, other): + for elt in ifilterfalse(self._data.__contains__, other): return False return True @@ -338,6 +316,9 @@ self._binary_sanity_check(other) return len(self) > len(other) and self.issuperset(other) + # We inherit object.__hash__, so we must deny this explicitly + __hash__ = None + # Assorted helpers def _binary_sanity_check(self, other): @@ -439,9 +420,6 @@ def __setstate__(self, data): self._data, = data - # We inherit object.__hash__, so we must deny this explicitly - __hash__ = None - # In-place union, intersection, differences. # Subtle: The xyz_update() functions deliberately return None, # as do all mutating operations on built-in container types. @@ -503,7 +481,7 @@ other = Set(other) if self is other: self.clear() - for elt in ifilter(data.has_key, other): + for elt in ifilter(data.__contains__, other): del data[elt] # Python dict-like mass mutations: update, clear Modified: python/branches/release26-maint/Lib/sunau.py ============================================================================== --- python/branches/release26-maint/Lib/sunau.py (original) +++ python/branches/release26-maint/Lib/sunau.py Mon Aug 2 02:24:26 2010 @@ -364,7 +364,8 @@ else: return 'not compressed' - def setparams(self, (nchannels, sampwidth, framerate, nframes, comptype, compname)): + def setparams(self, params): + nchannels, sampwidth, framerate, nframes, comptype, compname = params self.setnchannels(nchannels) self.setsampwidth(sampwidth) self.setframerate(framerate) Modified: python/branches/release26-maint/Lib/test/test_wsgiref.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_wsgiref.py (original) +++ python/branches/release26-maint/Lib/test/test_wsgiref.py Mon Aug 2 02:24:26 2010 @@ -523,7 +523,8 @@ "Content-Length: %d\r\n" "\r\n%s" % (h.error_status,len(h.error_body),h.error_body)) - self.failUnless(h.stderr.getvalue().find("AssertionError")<>-1) + self.assertTrue("AssertionError" in h.stderr.getvalue(), + "AssertionError not in stderr") def testErrorAfterOutput(self): MSG = "Some output has been sent" @@ -536,7 +537,8 @@ self.assertEqual(h.stdout.getvalue(), "Status: 200 OK\r\n" "\r\n"+MSG) - self.failUnless(h.stderr.getvalue().find("AssertionError")<>-1) + self.assertTrue("AssertionError" in h.stderr.getvalue(), + "AssertionError not in stderr") def testHeaderFormats(self): Modified: python/branches/release26-maint/Lib/wave.py ============================================================================== --- python/branches/release26-maint/Lib/wave.py (original) +++ python/branches/release26-maint/Lib/wave.py Mon Aug 2 02:24:26 2010 @@ -384,7 +384,8 @@ def getcompname(self): return self._compname - def setparams(self, (nchannels, sampwidth, framerate, nframes, comptype, compname)): + def setparams(self, params): + nchannels, sampwidth, framerate, nframes, comptype, compname = params if self._datawritten: raise Error, 'cannot change parameters after starting to write' self.setnchannels(nchannels) Modified: python/branches/release26-maint/Lib/webbrowser.py ============================================================================== --- python/branches/release26-maint/Lib/webbrowser.py (original) +++ python/branches/release26-maint/Lib/webbrowser.py Mon Aug 2 02:24:26 2010 @@ -650,7 +650,7 @@ for o, a in opts: if o == '-n': new_win = 1 elif o == '-t': new_win = 2 - if len(args) <> 1: + if len(args) != 1: print >>sys.stderr, usage sys.exit(1) Modified: python/branches/release26-maint/Misc/NEWS ============================================================================== --- python/branches/release26-maint/Misc/NEWS (original) +++ python/branches/release26-maint/Misc/NEWS Mon Aug 2 02:24:26 2010 @@ -89,6 +89,9 @@ Library ------- +- Issue #7092: Fix the DeprecationWarnings emitted by the standard library + when using the -3 flag. Patch by Florent Xicluna. + - Issue #7395: Fix tracebacks in pstats interactive browser. - Issue #1713: Fix os.path.ismount(), which returned true for symbolic links Modified: python/branches/release26-maint/Tools/compiler/astgen.py ============================================================================== --- python/branches/release26-maint/Tools/compiler/astgen.py (original) +++ python/branches/release26-maint/Tools/compiler/astgen.py Mon Aug 2 02:24:26 2010 @@ -105,12 +105,18 @@ def _gen_init(self, buf): if self.args: - print >> buf, " def __init__(self, %s, lineno=None):" % self.args + argtuple = '(' in self.args + args = self.args if not argtuple else ''.join(self.argnames) + print >> buf, " def __init__(self, %s, lineno=None):" % args else: print >> buf, " def __init__(self, lineno=None):" if self.argnames: - for name in self.argnames: - print >> buf, " self.%s = %s" % (name, name) + if argtuple: + for idx, name in enumerate(self.argnames): + print >> buf, " self.%s = %s[%s]" % (name, args, idx) + else: + for name in self.argnames: + print >> buf, " self.%s = %s" % (name, name) print >> buf, " self.lineno = lineno" # Copy the lines in self.init, indented four spaces. The rstrip() # business is to get rid of the four spaces if line happens to be From python-checkins at python.org Mon Aug 2 02:59:15 2010 From: python-checkins at python.org (raymond.hettinger) Date: Mon, 2 Aug 2010 02:59:15 +0200 (CEST) Subject: [Python-checkins] r83513 - python/branches/py3k/Doc/library/functools.rst Message-ID: <20100802005915.11FF9F520@mail.python.org> Author: raymond.hettinger Date: Mon Aug 2 02:59:14 2010 New Revision: 83513 Log: Add example applications for the LRU and LFU cache docs. Modified: python/branches/py3k/Doc/library/functools.rst Modified: python/branches/py3k/Doc/library/functools.rst ============================================================================== --- python/branches/py3k/Doc/library/functools.rst (original) +++ python/branches/py3k/Doc/library/functools.rst Mon Aug 2 02:59:14 2010 @@ -59,7 +59,8 @@ `_ is indicated when the pattern of calls does not change over time, when more the most common calls already seen are the best predictors of the - most common upcoming calls. + most common upcoming calls (for example, a phonelist of popular + help-lines may have access patterns that persist over time). .. versionadded:: 3.2 @@ -84,7 +85,9 @@ A `LRU (least recently used) cache `_ is indicated when the pattern of calls changes over time, such as - when more recent calls are the best predictors of upcoming calls. + when more recent calls are the best predictors of upcoming calls + (for example, the most popular articles on a news server tend to + change every day). .. versionadded:: 3.2 From python-checkins at python.org Mon Aug 2 03:17:10 2010 From: python-checkins at python.org (ezio.melotti) Date: Mon, 2 Aug 2010 03:17:10 +0200 (CEST) Subject: [Python-checkins] r83514 - python/branches/release26-maint Message-ID: <20100802011710.379F9EEBAC@mail.python.org> Author: ezio.melotti Date: Mon Aug 2 03:17:10 2010 New Revision: 83514 Log: Unblocked revisions 77841 via svnmerge ........ r77841 | ezio.melotti | 2010-01-30 09:22:54 +0200 (Sat, 30 Jan 2010) | 1 line #7092: silence py3k warnings for deprecated modules ........ Modified: python/branches/release26-maint/ (props changed) From python-checkins at python.org Mon Aug 2 03:33:30 2010 From: python-checkins at python.org (ezio.melotti) Date: Mon, 2 Aug 2010 03:33:30 +0200 (CEST) Subject: [Python-checkins] r83515 - in python/branches/release26-maint: Lib/test/test_anydbm.py Lib/test/test_bsddb.py Lib/test/test_commands.py Lib/test/test_hotshot.py Lib/test/test_linuxaudiodev.py Lib/test/test_mailbox.py Lib/test/test_multifile.py Lib/test/test_pyclbr.py Lib/test/test_shelve.py Lib/test/test_transformer.py Lib/test/test_urllib2_localnet.py Lib/test/test_urllibnet.py Lib/test/test_whichdb.py Lib/test/test_xmllib.py Message-ID: <20100802013330.7A83DEE9A4@mail.python.org> Author: ezio.melotti Date: Mon Aug 2 03:33:30 2010 New Revision: 83515 Log: Merged revisions 77841 via svnmerge from svn+ssh://pythondev at svn.python.org/python/trunk ........ r77841 | ezio.melotti | 2010-01-30 09:22:54 +0200 (Sat, 30 Jan 2010) | 1 line #7092: silence py3k warnings for deprecated modules ........ Modified: python/branches/release26-maint/ (props changed) python/branches/release26-maint/Lib/test/test_anydbm.py python/branches/release26-maint/Lib/test/test_bsddb.py python/branches/release26-maint/Lib/test/test_commands.py python/branches/release26-maint/Lib/test/test_hotshot.py python/branches/release26-maint/Lib/test/test_linuxaudiodev.py python/branches/release26-maint/Lib/test/test_mailbox.py python/branches/release26-maint/Lib/test/test_multifile.py python/branches/release26-maint/Lib/test/test_pyclbr.py python/branches/release26-maint/Lib/test/test_shelve.py python/branches/release26-maint/Lib/test/test_transformer.py python/branches/release26-maint/Lib/test/test_urllib2_localnet.py python/branches/release26-maint/Lib/test/test_urllibnet.py python/branches/release26-maint/Lib/test/test_whichdb.py python/branches/release26-maint/Lib/test/test_xmllib.py Modified: python/branches/release26-maint/Lib/test/test_anydbm.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_anydbm.py (original) +++ python/branches/release26-maint/Lib/test/test_anydbm.py Mon Aug 2 03:33:30 2010 @@ -5,12 +5,14 @@ import os import unittest -import anydbm import glob from test import test_support _fname = test_support.TESTFN +# Silence Py3k warning +anydbm = test_support.import_module('anydbm', deprecated=True) + def _delete_files(): # we don't know the precise name the underlying database uses # so we use glob to locate all names Modified: python/branches/release26-maint/Lib/test/test_bsddb.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_bsddb.py (original) +++ python/branches/release26-maint/Lib/test/test_bsddb.py Mon Aug 2 03:33:30 2010 @@ -9,6 +9,14 @@ import unittest from test import test_support +# Skip test if _bsddb wasn't built. +test_support.import_module('_bsddb') + +bsddb = test_support.import_module('bsddb', deprecated=True) +# Just so we know it's imported: +test_support.import_module('dbhash', deprecated=True) + + class TestBSDDB(unittest.TestCase): openflag = 'c' Modified: python/branches/release26-maint/Lib/test/test_commands.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_commands.py (original) +++ python/branches/release26-maint/Lib/test/test_commands.py Mon Aug 2 03:33:30 2010 @@ -9,7 +9,10 @@ warnings.filterwarnings('ignore', r".*commands.getstatus.. is deprecated", DeprecationWarning) -from test.test_support import TestSkipped, run_unittest, reap_children +from test.test_support import TestSkipped, run_unittest, reap_children, import_module + +# Silence Py3k warning +import_module('commands', deprecated=True) from commands import * # The module says: Modified: python/branches/release26-maint/Lib/test/test_hotshot.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_hotshot.py (original) +++ python/branches/release26-maint/Lib/test/test_hotshot.py Mon Aug 2 03:33:30 2010 @@ -1,5 +1,3 @@ -import hotshot -import hotshot.log import os import pprint import unittest @@ -9,6 +7,8 @@ from test import test_support +# Silence Py3k warning +hotshot = test_support.import_module('hotshot', deprecated=True) from hotshot.log import ENTER, EXIT, LINE from hotshot import stats Modified: python/branches/release26-maint/Lib/test/test_linuxaudiodev.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_linuxaudiodev.py (original) +++ python/branches/release26-maint/Lib/test/test_linuxaudiodev.py Mon Aug 2 03:33:30 2010 @@ -4,12 +4,13 @@ from test.test_support import findfile, TestSkipped, run_unittest import errno -linuxaudiodev = test_support.import_module('linuxaudiodev', deprecated=True) import sys -import sunaudio import audioop import unittest +linuxaudiodev = test_support.import_module('linuxaudiodev', deprecated=True) +sunaudio = test_support.import_module('sunaudio', deprecated=True) + SND_FORMAT_MULAW_8 = 1 class LinuxAudioDevTests(unittest.TestCase): Modified: python/branches/release26-maint/Lib/test/test_mailbox.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_mailbox.py (original) +++ python/branches/release26-maint/Lib/test/test_mailbox.py Mon Aug 2 03:33:30 2010 @@ -5,7 +5,6 @@ import socket import email import email.message -import rfc822 import re import StringIO from test import test_support @@ -17,6 +16,8 @@ except ImportError: pass +# Silence Py3k warning +rfc822 = test_support.import_module('rfc822', deprecated=True) class TestBase(unittest.TestCase): Modified: python/branches/release26-maint/Lib/test/test_multifile.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_multifile.py (original) +++ python/branches/release26-maint/Lib/test/test_multifile.py Mon Aug 2 03:33:30 2010 @@ -1,5 +1,5 @@ from test import test_support -import mimetools +mimetools = test_support.import_module('mimetools', deprecated=True) multifile = test_support.import_module('multifile', deprecated=True) import cStringIO Modified: python/branches/release26-maint/Lib/test/test_pyclbr.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_pyclbr.py (original) +++ python/branches/release26-maint/Lib/test/test_pyclbr.py Mon Aug 2 03:33:30 2010 @@ -2,7 +2,7 @@ Test cases for pyclbr.py Nick Mathewson ''' -from test.test_support import run_unittest +from test.test_support import run_unittest, import_module import sys from types import ClassType, FunctionType, MethodType, BuiltinFunctionType import pyclbr @@ -11,8 +11,10 @@ StaticMethodType = type(staticmethod(lambda: None)) ClassMethodType = type(classmethod(lambda c: None)) -# This next line triggers an error on old versions of pyclbr. +# Silence Py3k warning +import_module('commands', deprecated=True) +# This next line triggers an error on old versions of pyclbr. from commands import getstatus # Here we test the python class browser code. @@ -40,16 +42,16 @@ def assertHaskey(self, obj, key, ignore): - ''' succeed iff obj.has_key(key) or key in ignore. ''' + ''' succeed iff key in obj or key in ignore. ''' if key in ignore: return - if not obj.has_key(key): - print >>sys.stderr, "***",key - self.failUnless(obj.has_key(key)) + if key not in obj: + print >>sys.stderr, "***", key + self.assertTrue(key in obj) def assertEqualsOrIgnored(self, a, b, ignore): ''' succeed iff a == b or a in ignore or b in ignore ''' if a not in ignore and b not in ignore: - self.assertEquals(a, b) + self.assertEqual(a, b) def checkModule(self, moduleName, module=None, ignore=()): ''' succeed iff pyclbr.readmodule_ex(modulename) corresponds @@ -149,7 +151,9 @@ def test_easy(self): self.checkModule('pyclbr') self.checkModule('doctest') - self.checkModule('rfc822') + # Silence Py3k warning + rfc822 = import_module('rfc822', deprecated=True) + self.checkModule('rfc822', rfc822) self.checkModule('difflib') def test_decorators(self): Modified: python/branches/release26-maint/Lib/test/test_shelve.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_shelve.py (original) +++ python/branches/release26-maint/Lib/test/test_shelve.py Mon Aug 2 03:33:30 2010 @@ -4,6 +4,8 @@ import glob from test import test_support +test_support.import_module('anydbm', deprecated=True) + class TestCase(unittest.TestCase): fn = "shelftemp" + os.extsep + "db" Modified: python/branches/release26-maint/Lib/test/test_transformer.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_transformer.py (original) +++ python/branches/release26-maint/Lib/test/test_transformer.py Mon Aug 2 03:33:30 2010 @@ -1,5 +1,8 @@ import unittest from test import test_support + +# Silence Py3k warning +test_support.import_module('compiler', deprecated=True) from compiler import transformer, ast from compiler import compile Modified: python/branches/release26-maint/Lib/test/test_urllib2_localnet.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_urllib2_localnet.py (original) +++ python/branches/release26-maint/Lib/test/test_urllib2_localnet.py Mon Aug 2 03:33:30 2010 @@ -1,6 +1,5 @@ #!/usr/bin/env python -import mimetools import threading import urlparse import urllib2 @@ -8,6 +7,7 @@ import unittest import hashlib from test import test_support +mimetools = test_support.import_module('mimetools', deprecated=True) # Loopback http server infrastructure @@ -154,13 +154,13 @@ if len(self._users) == 0: return True - if not request_handler.headers.has_key('Proxy-Authorization'): + if 'Proxy-Authorization' not in request_handler.headers: return self._return_auth_challenge(request_handler) else: auth_dict = self._create_auth_dict( request_handler.headers['Proxy-Authorization'] ) - if self._users.has_key(auth_dict["username"]): + if auth_dict["username"] in self._users: password = self._users[ auth_dict["username"] ] else: return self._return_auth_challenge(request_handler) Modified: python/branches/release26-maint/Lib/test/test_urllibnet.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_urllibnet.py (original) +++ python/branches/release26-maint/Lib/test/test_urllibnet.py Mon Aug 2 03:33:30 2010 @@ -7,7 +7,7 @@ import urllib import sys import os -import mimetools +mimetools = test_support.import_module("mimetools", deprecated=True) import time Modified: python/branches/release26-maint/Lib/test/test_whichdb.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_whichdb.py (original) +++ python/branches/release26-maint/Lib/test/test_whichdb.py Mon Aug 2 03:33:30 2010 @@ -7,11 +7,13 @@ import test.test_support import unittest import whichdb -import anydbm import glob _fname = test.test_support.TESTFN +# Silence Py3k warning +anydbm = test.test_support.import_module('anydbm', deprecated=True) + def _delete_files(): # we don't know the precise name the underlying database uses # so we use glob to locate all names @@ -37,8 +39,9 @@ # we define a new test method for each # candidate database module. try: - mod = __import__(name) - except ImportError: + # Silence Py3k warning + mod = test.test_support.import_module(name, deprecated=True) + except test.test_support.TestSkipped: continue def test_whichdb_name(self, name=name, mod=mod): Modified: python/branches/release26-maint/Lib/test/test_xmllib.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_xmllib.py (original) +++ python/branches/release26-maint/Lib/test/test_xmllib.py Mon Aug 2 03:33:30 2010 @@ -15,13 +15,10 @@ nsdoc = "" -import warnings -warnings.filterwarnings("ignore", ".* xmllib .* obsolete.*", - DeprecationWarning, r'xmllib$') - from test import test_support import unittest -import xmllib +# Silence Py3k warning +xmllib = test_support.import_module('xmllib', deprecated=True) class XMLParserTestCase(unittest.TestCase): From python-checkins at python.org Mon Aug 2 03:43:42 2010 From: python-checkins at python.org (raymond.hettinger) Date: Mon, 2 Aug 2010 03:43:42 +0200 (CEST) Subject: [Python-checkins] r83516 - python/branches/py3k/Doc/whatsnew/3.2.rst Message-ID: <20100802014342.1DC66EEBCC@mail.python.org> Author: raymond.hettinger Date: Mon Aug 2 03:43:41 2010 New Revision: 83516 Log: Demonstrate the caching decorators in whatsnew. Modified: python/branches/py3k/Doc/whatsnew/3.2.rst Modified: python/branches/py3k/Doc/whatsnew/3.2.rst ============================================================================== --- python/branches/py3k/Doc/whatsnew/3.2.rst (original) +++ python/branches/py3k/Doc/whatsnew/3.2.rst Mon Aug 2 03:43:41 2010 @@ -66,6 +66,38 @@ New, Improved, and Deprecated Modules ===================================== +* The functools module now includes two new decorators for caching function + calls, :func:`functools.lru_cache` and :func:`functools.lfu_cache`. These can + save repeated queries to an external resource whenever the results are + expected to be the same. + + For example, adding an LFU decorator to a database query function can save + database accesses for the most popular searches:: + + @functools.lfu_cache(maxsize=50) + def get_phone_number(name): + c = conn.cursor() + c.execute('SELECT phonenumber FROM phonelist WHERE name=?', (name,)) + return c.fetchone()[0] + + The LFU (least-frequently-used) cache gives best results when the distribution + of popular queries tends to remain the same over time. In contrast, the LRU + (least-recently-used) cache gives best results when the distribution changes + over time (for example, the most popular news articles change each day as + newer articles are added). + + The two caching decorators can be composed (nested) to handle hybrid cases + that have both long-term access patterns and some short-term access trends. + For example, music searches can reflect both long-term patterns (popular + classics) and short-term trends (new releases):: + + @functools.lfu_cache(maxsize=500) + @functools.lru_cache(maxsize=100) + def find_music(song): + ... + + (Contributed by Raymond Hettinger) + * The previously deprecated :func:`contextlib.nested` function has been removed in favor of a plain :keyword:`with` statement which can accept multiple context managers. The latter technique is faster From python-checkins at python.org Mon Aug 2 04:16:49 2010 From: python-checkins at python.org (ezio.melotti) Date: Mon, 2 Aug 2010 04:16:49 +0200 (CEST) Subject: [Python-checkins] r83517 - python/branches/release26-maint Message-ID: <20100802021649.733DAEEBB8@mail.python.org> Author: ezio.melotti Date: Mon Aug 2 04:16:49 2010 New Revision: 83517 Log: Unblocked revisions 77871,77910,77913 via svnmerge ........ r77871 | ezio.melotti | 2010-01-31 13:46:54 +0200 (Sun, 31 Jan 2010) | 1 line #7092: silence more -3 and -Wd warnings ........ r77910 | ezio.melotti | 2010-02-02 10:37:35 +0200 (Tue, 02 Feb 2010) | 1 line #7092: silence py3k warnings for bsddb. Patch by Florent Xicluna. ........ r77913 | ezio.melotti | 2010-02-02 19:34:37 +0200 (Tue, 02 Feb 2010) | 1 line #7092: Silence py3k warnings in test_exceptions and test_pep352. Patch by Florent Xicluna. ........ Modified: python/branches/release26-maint/ (props changed) From python-checkins at python.org Mon Aug 2 05:14:27 2010 From: python-checkins at python.org (ezio.melotti) Date: Mon, 2 Aug 2010 05:14:27 +0200 (CEST) Subject: [Python-checkins] r83518 - in python/branches/release26-maint: Lib/bsddb/dbtables.py Lib/bsddb/test/test_basics.py Lib/bsddb/test/test_compare.py Lib/bsddb/test/test_dbobj.py Lib/bsddb/test/test_dbshelve.py Lib/bsddb/test/test_join.py Lib/bsddb/test/test_recno.py Lib/cgi.py Lib/test/test_bsddb3.py Lib/test/test_cgi.py Lib/test/test_coercion.py Lib/test/test_distutils.py Lib/test/test_exceptions.py Lib/test/test_pep352.py Lib/test/test_userstring.py Message-ID: <20100802031427.76184EEAA7@mail.python.org> Author: ezio.melotti Date: Mon Aug 2 05:14:27 2010 New Revision: 83518 Log: Merged revisions 77871,77910,77913 via svnmerge from svn+ssh://pythondev at svn.python.org/python/trunk ........ r77871 | ezio.melotti | 2010-01-31 13:46:54 +0200 (Sun, 31 Jan 2010) | 1 line #7092: silence more -3 and -Wd warnings ........ r77910 | ezio.melotti | 2010-02-02 10:37:35 +0200 (Tue, 02 Feb 2010) | 1 line #7092: silence py3k warnings for bsddb. Patch by Florent Xicluna. ........ r77913 | ezio.melotti | 2010-02-02 19:34:37 +0200 (Tue, 02 Feb 2010) | 1 line #7092: Silence py3k warnings in test_exceptions and test_pep352. Patch by Florent Xicluna. ........ Modified: python/branches/release26-maint/ (props changed) python/branches/release26-maint/Lib/bsddb/dbtables.py python/branches/release26-maint/Lib/bsddb/test/test_basics.py python/branches/release26-maint/Lib/bsddb/test/test_compare.py python/branches/release26-maint/Lib/bsddb/test/test_dbobj.py python/branches/release26-maint/Lib/bsddb/test/test_dbshelve.py python/branches/release26-maint/Lib/bsddb/test/test_join.py python/branches/release26-maint/Lib/bsddb/test/test_recno.py python/branches/release26-maint/Lib/cgi.py python/branches/release26-maint/Lib/test/test_bsddb3.py python/branches/release26-maint/Lib/test/test_cgi.py python/branches/release26-maint/Lib/test/test_coercion.py python/branches/release26-maint/Lib/test/test_distutils.py python/branches/release26-maint/Lib/test/test_exceptions.py python/branches/release26-maint/Lib/test/test_pep352.py python/branches/release26-maint/Lib/test/test_userstring.py Modified: python/branches/release26-maint/Lib/bsddb/dbtables.py ============================================================================== --- python/branches/release26-maint/Lib/bsddb/dbtables.py (original) +++ python/branches/release26-maint/Lib/bsddb/dbtables.py Mon Aug 2 05:14:27 2010 @@ -332,7 +332,7 @@ except db.DBError, dberror: if txn: txn.abort() - if sys.version_info[0] < 3 : + if sys.version_info < (2, 6) : raise TableDBError, dberror[1] else : raise TableDBError, dberror.args[1] @@ -416,7 +416,7 @@ except db.DBError, dberror: if txn: txn.abort() - if sys.version_info[0] < 3 : + if sys.version_info < (2, 6) : raise TableDBError, dberror[1] else : raise TableDBError, dberror.args[1] @@ -499,7 +499,7 @@ if txn: txn.abort() self.db.delete(_rowid_key(table, rowid)) - if sys.version_info[0] < 3 : + if sys.version_info < (2, 6) : raise TableDBError, dberror[1], info[2] else : raise TableDBError, dberror.args[1], info[2] @@ -554,7 +554,7 @@ raise except db.DBError, dberror: - if sys.version_info[0] < 3 : + if sys.version_info < (2, 6) : raise TableDBError, dberror[1] else : raise TableDBError, dberror.args[1] @@ -598,7 +598,7 @@ txn.abort() raise except db.DBError, dberror: - if sys.version_info[0] < 3 : + if sys.version_info < (2, 6) : raise TableDBError, dberror[1] else : raise TableDBError, dberror.args[1] @@ -621,7 +621,7 @@ columns = self.__tablecolumns[table] matching_rowids = self.__Select(table, columns, conditions) except db.DBError, dberror: - if sys.version_info[0] < 3 : + if sys.version_info < (2, 6) : raise TableDBError, dberror[1] else : raise TableDBError, dberror.args[1] @@ -677,7 +677,7 @@ # leave all unknown condition callables alone as equals return 0 - if sys.version_info[0] < 3 : + if sys.version_info < (2, 6) : conditionlist = conditions.items() conditionlist.sort(cmp_conditions) else : # Insertion Sort. Please, improve @@ -749,7 +749,7 @@ rowdata[column] = self.db.get( _data_key(table, column, rowid)) except db.DBError, dberror: - if sys.version_info[0] < 3 : + if sys.version_info < (2, 6) : if dberror[0] != db.DB_NOTFOUND: raise else : Modified: python/branches/release26-maint/Lib/bsddb/test/test_basics.py ============================================================================== --- python/branches/release26-maint/Lib/bsddb/test/test_basics.py (original) +++ python/branches/release26-maint/Lib/bsddb/test/test_basics.py Mon Aug 2 05:14:27 2010 @@ -139,11 +139,7 @@ try: d.delete('abcd') except db.DBNotFoundError, val: - import sys - if sys.version_info[0] < 3 : - self.assertEqual(val[0], db.DB_NOTFOUND) - else : - self.assertEqual(val.args[0], db.DB_NOTFOUND) + self.assertEqual(val.args[0], db.DB_NOTFOUND) if verbose: print val else: self.fail("expected exception") @@ -162,11 +158,7 @@ try: d.put('abcd', 'this should fail', flags=db.DB_NOOVERWRITE) except db.DBKeyExistError, val: - import sys - if sys.version_info[0] < 3 : - self.assertEqual(val[0], db.DB_KEYEXIST) - else : - self.assertEqual(val.args[0], db.DB_KEYEXIST) + self.assertEqual(val.args[0], db.DB_KEYEXIST) if verbose: print val else: self.fail("expected exception") @@ -301,11 +293,7 @@ rec = c.next() except db.DBNotFoundError, val: if get_raises_error: - import sys - if sys.version_info[0] < 3 : - self.assertEqual(val[0], db.DB_NOTFOUND) - else : - self.assertEqual(val.args[0], db.DB_NOTFOUND) + self.assertEqual(val.args[0], db.DB_NOTFOUND) if verbose: print val rec = None else: @@ -326,11 +314,7 @@ rec = c.prev() except db.DBNotFoundError, val: if get_raises_error: - import sys - if sys.version_info[0] < 3 : - self.assertEqual(val[0], db.DB_NOTFOUND) - else : - self.assertEqual(val.args[0], db.DB_NOTFOUND) + self.assertEqual(val.args[0], db.DB_NOTFOUND) if verbose: print val rec = None else: @@ -353,11 +337,7 @@ try: n = c.set('bad key') except db.DBNotFoundError, val: - import sys - if sys.version_info[0] < 3 : - self.assertEqual(val[0], db.DB_NOTFOUND) - else : - self.assertEqual(val.args[0], db.DB_NOTFOUND) + self.assertEqual(val.args[0], db.DB_NOTFOUND) if verbose: print val else: if set_raises_error: @@ -371,11 +351,7 @@ try: n = c.get_both('0404', 'bad data') except db.DBNotFoundError, val: - import sys - if sys.version_info[0] < 3 : - self.assertEqual(val[0], db.DB_NOTFOUND) - else : - self.assertEqual(val.args[0], db.DB_NOTFOUND) + self.assertEqual(val.args[0], db.DB_NOTFOUND) if verbose: print val else: if get_raises_error: @@ -404,11 +380,7 @@ rec = c.current() except db.DBKeyEmptyError, val: if get_raises_error: - import sys - if sys.version_info[0] < 3 : - self.assertEqual(val[0], db.DB_KEYEMPTY) - else : - self.assertEqual(val.args[0], db.DB_KEYEMPTY) + self.assertEqual(val.args[0], db.DB_KEYEMPTY) if verbose: print val else: self.fail("unexpected DBKeyEmptyError") @@ -451,13 +423,9 @@ print "attempting to use a closed cursor's %s method" % \ method # a bug may cause a NULL pointer dereference... - apply(getattr(c, method), args) + getattr(c, method)(*args) except db.DBError, val: - import sys - if sys.version_info[0] < 3 : - self.assertEqual(val[0], 0) - else : - self.assertEqual(val.args[0], 0) + self.assertEqual(val.args[0], 0) if verbose: print val else: self.fail("no exception raised when using a buggy cursor's" @@ -710,10 +678,10 @@ pass statDict = self.env.log_stat(0); - self.assert_(statDict.has_key('magic')) - self.assert_(statDict.has_key('version')) - self.assert_(statDict.has_key('cur_file')) - self.assert_(statDict.has_key('region_nowait')) + self.assertTrue('magic' in statDict) + self.assertTrue('version' in statDict) + self.assertTrue('cur_file' in statDict) + self.assertTrue('region_nowait' in statDict) # must have at least one log file present: logs = self.env.log_archive(db.DB_ARCH_ABS | db.DB_ARCH_LOG) Modified: python/branches/release26-maint/Lib/bsddb/test/test_compare.py ============================================================================== --- python/branches/release26-maint/Lib/bsddb/test/test_compare.py (original) +++ python/branches/release26-maint/Lib/bsddb/test/test_compare.py Mon Aug 2 05:14:27 2010 @@ -30,7 +30,7 @@ data = expected_data[:] import sys - if sys.version_info[0] < 3 : + if sys.version_info[:3] < (2, 6, 0): if sys.version_info[:3] < (2, 4, 0): data.sort(comparator) else : @@ -47,7 +47,7 @@ data2.append(i) data = data2 - self.failUnless (data == expected_data, + self.assertEqual (data, expected_data, "comparator `%s' is not right: %s vs. %s" % (comparator, expected_data, data)) def test_lexical_comparator (self): @@ -115,14 +115,14 @@ rec = curs.first () while rec: key, ignore = rec - self.failUnless (index < len (expected), + self.assertTrue (index < len (expected), "to many values returned from cursor") - self.failUnless (expected[index] == key, + self.assertEqual (expected[index], key, "expected value `%s' at %d but got `%s'" % (expected[index], index, key)) index = index + 1 rec = curs.next () - self.failUnless (index == len (expected), + self.assertEqual (index, len (expected), "not enough values returned from cursor") finally: curs.close () Modified: python/branches/release26-maint/Lib/bsddb/test/test_dbobj.py ============================================================================== --- python/branches/release26-maint/Lib/bsddb/test/test_dbobj.py (original) +++ python/branches/release26-maint/Lib/bsddb/test/test_dbobj.py Mon Aug 2 05:14:27 2010 @@ -27,7 +27,7 @@ def put(self, key, *args, **kwargs): key = key.upper() # call our parent classes put method with an upper case key - return apply(dbobj.DB.put, (self, key) + args, kwargs) + return dbobj.DB.put(self, key, *args, **kwargs) self.env = TestDBEnv() self.env.open(self.homeDir, db.DB_CREATE | db.DB_INIT_MPOOL) self.db = TestDB(self.env) Modified: python/branches/release26-maint/Lib/bsddb/test/test_dbshelve.py ============================================================================== --- python/branches/release26-maint/Lib/bsddb/test/test_dbshelve.py (original) +++ python/branches/release26-maint/Lib/bsddb/test/test_dbshelve.py Mon Aug 2 05:14:27 2010 @@ -5,6 +5,7 @@ import os, string import random import unittest +import warnings from test_all import db, dbshelve, test_support, verbose, \ @@ -117,15 +118,11 @@ dbvalues = d.values() self.assertEqual(len(dbvalues), len(d.keys())) - import sys - if sys.version_info[0] < 3 : - values.sort() - dbvalues.sort() - self.assertEqual(values, dbvalues) - else : # XXX: Convert all to strings. Please, improve - values.sort(key=lambda x : str(x)) - dbvalues.sort(key=lambda x : str(x)) - self.assertEqual(repr(values), repr(dbvalues)) + with warnings.catch_warnings(): + warnings.filterwarnings('ignore', + 'comparing unequal types not supported', + DeprecationWarning) + self.assertEqual(sorted(values), sorted(dbvalues)) items = d.items() self.assertEqual(len(items), len(values)) Modified: python/branches/release26-maint/Lib/bsddb/test/test_join.py ============================================================================== --- python/branches/release26-maint/Lib/bsddb/test/test_join.py (original) +++ python/branches/release26-maint/Lib/bsddb/test/test_join.py Mon Aug 2 05:14:27 2010 @@ -51,13 +51,13 @@ # create and populate primary index priDB = db.DB(self.env) priDB.open(self.filename, "primary", db.DB_BTREE, db.DB_CREATE) - map(lambda t, priDB=priDB: apply(priDB.put, t), ProductIndex) + map(lambda t, priDB=priDB: priDB.put(*t), ProductIndex) # create and populate secondary index secDB = db.DB(self.env) secDB.set_flags(db.DB_DUP | db.DB_DUPSORT) secDB.open(self.filename, "secondary", db.DB_BTREE, db.DB_CREATE) - map(lambda t, secDB=secDB: apply(secDB.put, t), ColorIndex) + map(lambda t, secDB=secDB: secDB.put(*t), ColorIndex) sCursor = None jCursor = None Modified: python/branches/release26-maint/Lib/bsddb/test/test_recno.py ============================================================================== --- python/branches/release26-maint/Lib/bsddb/test/test_recno.py (original) +++ python/branches/release26-maint/Lib/bsddb/test/test_recno.py Mon Aug 2 05:14:27 2010 @@ -60,11 +60,7 @@ try: data = d[0] # This should raise a KeyError!?!?! except db.DBInvalidArgError, val: - import sys - if sys.version_info[0] < 3 : - self.assertEqual(val[0], db.EINVAL) - else : - self.assertEqual(val.args[0], db.EINVAL) + self.assertEqual(val.args[0], db.EINVAL) if verbose: print val else: self.fail("expected exception") @@ -269,11 +265,7 @@ try: # this one will fail d.append('bad' * 20) except db.DBInvalidArgError, val: - import sys - if sys.version_info[0] < 3 : - self.assertEqual(val[0], db.EINVAL) - else : - self.assertEqual(val.args[0], db.EINVAL) + self.assertEqual(val.args[0], db.EINVAL) if verbose: print val else: self.fail("expected exception") Modified: python/branches/release26-maint/Lib/cgi.py ============================================================================== --- python/branches/release26-maint/Lib/cgi.py (original) +++ python/branches/release26-maint/Lib/cgi.py Mon Aug 2 05:14:27 2010 @@ -172,7 +172,7 @@ else: qs = "" environ['QUERY_STRING'] = qs # XXX Shouldn't, really - return parse_qs(qs, keep_blank_values, strict_parsing) + return urlparse.parse_qs(qs, keep_blank_values, strict_parsing) # parse query string function called from urlparse, Modified: python/branches/release26-maint/Lib/test/test_bsddb3.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_bsddb3.py (original) +++ python/branches/release26-maint/Lib/test/test_bsddb3.py Mon Aug 2 05:14:27 2010 @@ -7,7 +7,13 @@ import tempfile import time import unittest -from test.test_support import requires, verbose, run_unittest, unlink, rmtree +from test.test_support import (requires, verbose, run_unittest, unlink, rmtree, + import_module) + +# Skip test if _bsddb module was not built. +import_module('_bsddb') +# Silence Py3k warning +import_module('bsddb', deprecated=True) # When running as a script instead of within the regrtest framework, skip the # requires test, since it's obvious we want to run them. Modified: python/branches/release26-maint/Lib/test/test_cgi.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_cgi.py (original) +++ python/branches/release26-maint/Lib/test/test_cgi.py Mon Aug 2 05:14:27 2010 @@ -1,4 +1,4 @@ -from test.test_support import run_unittest +from test.test_support import run_unittest, check_warnings import cgi import os import sys @@ -102,11 +102,6 @@ }) ] -def norm(list): - if type(list) == type([]): - list.sort() - return list - def first_elts(list): return map(lambda x:x[0], list) @@ -141,18 +136,18 @@ if type(expect) == type({}): # test dict interface self.assertEqual(len(expect), len(fcd)) - self.assertEqual(norm(expect.keys()), norm(fcd.keys())) - self.assertEqual(norm(expect.values()), norm(fcd.values())) - self.assertEqual(norm(expect.items()), norm(fcd.items())) + self.assertEqual(sorted(expect.keys()), sorted(fcd.keys())) + self.assertEqual(sorted(expect.values()), sorted(fcd.values())) + self.assertEqual(sorted(expect.items()), sorted(fcd.items())) self.assertEqual(fcd.get("nonexistent field", "default"), "default") self.assertEqual(len(sd), len(fs)) - self.assertEqual(norm(sd.keys()), norm(fs.keys())) + self.assertEqual(sorted(sd.keys()), sorted(fs.keys())) self.assertEqual(fs.getvalue("nonexistent field", "default"), "default") # test individual fields for key in expect.keys(): expect_val = expect[key] self.assert_(fcd.has_key(key)) - self.assertEqual(norm(fcd[key]), norm(expect[key])) + self.assertEqual(sorted(fcd[key]), sorted(expect[key])) self.assertEqual(fcd.get(key, "default"), fcd[key]) self.assert_(fs.has_key(key)) if len(expect_val) > 1: @@ -168,12 +163,12 @@ self.assert_(single_value) self.assertEqual(val, expect_val[0]) self.assertEqual(fs.getvalue(key), expect_val[0]) - self.assertEqual(norm(sd.getlist(key)), norm(expect_val)) + self.assertEqual(sorted(sd.getlist(key)), sorted(expect_val)) if single_value: - self.assertEqual(norm(sd.values()), - first_elts(norm(expect.values()))) - self.assertEqual(norm(sd.items()), - first_second_elts(norm(expect.items()))) + self.assertEqual(sorted(sd.values()), + sorted(first_elts(expect.values()))) + self.assertEqual(sorted(sd.items()), + sorted(first_second_elts(expect.items()))) def test_weird_formcontentdict(self): # Test the weird FormContentDict classes @@ -184,7 +179,7 @@ self.assertEqual(d[k], v) for k, v in d.items(): self.assertEqual(expect[k], v) - self.assertEqual(norm(expect.values()), norm(d.values())) + self.assertEqual(sorted(expect.values()), sorted(d.values())) def test_log(self): cgi.log("Testing") @@ -345,14 +340,16 @@ self.assertEqual(result, v) def test_deprecated_parse_qs(self): - # this func is moved to urlparse, this is just a sanity check - self.assertEqual({'a': ['A1'], 'B': ['B3'], 'b': ['B2']}, - cgi.parse_qs('a=A1&b=B2&B=B3')) + with check_warnings(): + # this func is moved to urlparse, this is just a sanity check + 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 - self.assertEqual([('a', 'A1'), ('b', 'B2'), ('B', 'B3')], - cgi.parse_qsl('a=A1&b=B2&B=B3')) + with check_warnings(): + # this func is moved to urlparse, this is just a sanity check + self.assertEqual([('a', 'A1'), ('b', 'B2'), ('B', 'B3')], + cgi.parse_qsl('a=A1&b=B2&B=B3')) def test_parse_header(self): self.assertEqual( Modified: python/branches/release26-maint/Lib/test/test_coercion.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_coercion.py (original) +++ python/branches/release26-maint/Lib/test/test_coercion.py Mon Aug 2 05:14:27 2010 @@ -223,8 +223,10 @@ infix_results[key] = res - -process_infix_results() +with warnings.catch_warnings(): + warnings.filterwarnings("ignore", "classic int division", + DeprecationWarning) + process_infix_results() # now infix_results has two lists of results for every pairing. prefix_binops = [ 'divmod' ] @@ -337,11 +339,12 @@ raise exc def test_main(): - warnings.filterwarnings("ignore", - r'complex divmod\(\), // and % are deprecated', - DeprecationWarning, - r'test.test_coercion$') - run_unittest(CoercionTest) + with warnings.catch_warnings(): + warnings.filterwarnings("ignore", "complex divmod.., // and % " + "are deprecated", DeprecationWarning) + warnings.filterwarnings("ignore", "classic (int|long) division", + DeprecationWarning) + run_unittest(CoercionTest) if __name__ == "__main__": test_main() Modified: python/branches/release26-maint/Lib/test/test_distutils.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_distutils.py (original) +++ python/branches/release26-maint/Lib/test/test_distutils.py Mon Aug 2 05:14:27 2010 @@ -7,10 +7,15 @@ import distutils.tests import test.test_support +import warnings def test_main(): - test.test_support.run_unittest(distutils.tests.test_suite()) + with warnings.catch_warnings(): + warnings.filterwarnings("ignore", + "distutils.sysconfig.\w+ is deprecated", + DeprecationWarning) + test.test_support.run_unittest(distutils.tests.test_suite()) if __name__ == "__main__": Modified: python/branches/release26-maint/Lib/test/test_exceptions.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_exceptions.py (original) +++ python/branches/release26-maint/Lib/test/test_exceptions.py Mon Aug 2 05:14:27 2010 @@ -7,7 +7,7 @@ import warnings from test.test_support import TESTFN, unlink, run_unittest, captured_output -from test.test_pep352 import ignore_message_warning +from test.test_pep352 import ignore_deprecation_warnings # XXX This is not really enough, each *operation* should be tested! @@ -17,6 +17,7 @@ # Reloading the built-in exceptions module failed prior to Py2.2, while it # should act the same as reloading built-in sys. try: + from imp import reload import exceptions reload(exceptions) except ImportError, e: @@ -108,11 +109,11 @@ self.assertRaises(ValueError, chr, 10000) self.raise_catch(ZeroDivisionError, "ZeroDivisionError") - try: x = 1/0 + try: x = 1 // 0 except ZeroDivisionError: pass self.raise_catch(Exception, "Exception") - try: x = 1/0 + try: x = 1 // 0 except Exception, e: pass def testSyntaxErrorMessage(self): @@ -197,6 +198,7 @@ self.failUnlessEqual(WindowsError(1001, "message").errno, 22) self.failUnlessEqual(WindowsError(1001, "message").winerror, 1001) + @ignore_deprecation_warnings def testAttributes(self): # test that exception attributes are happy @@ -274,34 +276,32 @@ except NameError: pass - with warnings.catch_warnings(): - ignore_message_warning() - for exc, args, expected in exceptionList: - try: - raise exc(*args) - except BaseException, e: - if type(e) is not exc: - raise - # Verify module name - self.assertEquals(type(e).__module__, 'exceptions') - # 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 - for p in pickle, cPickle: - for protocol in range(p.HIGHEST_PROTOCOL + 1): - new = p.loads(p.dumps(e, protocol)) - for checkArgName in expected: - got = repr(getattr(new, checkArgName)) - want = repr(expected[checkArgName]) - self.assertEquals(got, want, - 'pickled "%r", attribute "%s"' % - (e, checkArgName)) + for exc, args, expected in exceptionList: + try: + raise exc(*args) + except BaseException, e: + if type(e) is not exc: + raise + # Verify module name + self.assertEquals(type(e).__module__, 'exceptions') + # 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 + for p in pickle, cPickle: + for protocol in range(p.HIGHEST_PROTOCOL + 1): + new = p.loads(p.dumps(e, protocol)) + for checkArgName in expected: + got = repr(getattr(new, checkArgName)) + want = repr(expected[checkArgName]) + self.assertEquals(got, want, + 'pickled "%r", attribute "%s"' % + (e, checkArgName)) def testDeprecatedMessageAttribute(self): @@ -329,6 +329,7 @@ del exc.message self.assertRaises(AttributeError, getattr, exc, "message") + @ignore_deprecation_warnings def testPickleMessageAttribute(self): # Pickling with message attribute must work, as well. e = Exception("foo") @@ -336,18 +337,18 @@ f.message = "bar" for p in pickle, cPickle: ep = p.loads(p.dumps(e)) - with warnings.catch_warnings(): - ignore_message_warning() - self.assertEqual(ep.message, "foo") + self.assertEqual(ep.message, "foo") fp = p.loads(p.dumps(f)) self.assertEqual(fp.message, "bar") + @ignore_deprecation_warnings def testSlicing(self): # Test that you can slice an exception directly instead of requiring # going through the 'args' attribute. args = (1, 2, 3) exc = BaseException(*args) self.failUnlessEqual(exc[:], args) + self.assertEqual(exc.args[:], args) def testKeywordArgs(self): # test that builtin exception don't take keyword args, Modified: python/branches/release26-maint/Lib/test/test_pep352.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_pep352.py (original) +++ python/branches/release26-maint/Lib/test/test_pep352.py Mon Aug 2 05:14:27 2010 @@ -4,14 +4,27 @@ import warnings from test.test_support import run_unittest import os +import sys from platform import system as platform_system -def ignore_message_warning(): - """Ignore the DeprecationWarning for BaseException.message.""" - warnings.resetwarnings() - warnings.filterwarnings("ignore", "BaseException.message", - DeprecationWarning) +DEPRECATION_WARNINGS = ["BaseException.message has been deprecated"] +if sys.py3kwarning: + DEPRECATION_WARNINGS.extend( + ["exceptions must derive from BaseException", + "catching classes that don't inherit from BaseException is not allowed", + "__get(item|slice)__ not supported for exception classes"]) + +# Silence Py3k and other deprecation warnings +def ignore_deprecation_warnings(func): + """Ignore the known DeprecationWarnings.""" + def wrapper(*args, **kw): + with warnings.catch_warnings(): + warnings.resetwarnings() + for text in DEPRECATION_WARNINGS: + warnings.filterwarnings("ignore", text, DeprecationWarning) + return func(*args, **kw) + return wrapper class ExceptionClassTests(unittest.TestCase): @@ -21,13 +34,11 @@ def test_builtins_new_style(self): self.failUnless(issubclass(Exception, object)) + @ignore_deprecation_warnings def verify_instance_interface(self, ins): - with warnings.catch_warnings(): - ignore_message_warning() - for attr in ("args", "message", "__str__", "__repr__", - "__getitem__"): - self.failUnless(hasattr(ins, attr), - "%s missing %s attribute" % + for attr in ("args", "message", "__str__", "__repr__", "__getitem__"): + self.assertTrue(hasattr(ins, attr), + "%s missing %s attribute" % (ins.__class__.__name__, attr)) def test_inheritance(self): @@ -91,43 +102,39 @@ self.failUnlessEqual(given, expected, "%s: %s != %s" % (test_name, given, expected)) + @ignore_deprecation_warnings def test_interface_single_arg(self): # Make sure interface works properly when given a single argument arg = "spam" exc = Exception(arg) - with warnings.catch_warnings(): - ignore_message_warning() - results = ([len(exc.args), 1], [exc.args[0], arg], - [exc.message, arg], - [str(exc), str(arg)], [unicode(exc), unicode(arg)], - [repr(exc), exc.__class__.__name__ + repr(exc.args)], [exc[0], - arg]) - self.interface_test_driver(results) + results = ([len(exc.args), 1], [exc.args[0], arg], [exc.message, arg], + [str(exc), str(arg)], [unicode(exc), unicode(arg)], + [repr(exc), exc.__class__.__name__ + repr(exc.args)], + [exc[0], arg]) + self.interface_test_driver(results) + @ignore_deprecation_warnings def test_interface_multi_arg(self): # Make sure interface correct when multiple arguments given arg_count = 3 args = tuple(range(arg_count)) exc = Exception(*args) - with warnings.catch_warnings(): - ignore_message_warning() - results = ([len(exc.args), arg_count], [exc.args, args], - [exc.message, ''], [str(exc), str(args)], - [unicode(exc), unicode(args)], - [repr(exc), exc.__class__.__name__ + repr(exc.args)], - [exc[-1], args[-1]]) - self.interface_test_driver(results) + results = ([len(exc.args), arg_count], [exc.args, args], + [exc.message, ''], [str(exc), str(args)], + [unicode(exc), unicode(args)], + [repr(exc), exc.__class__.__name__ + repr(exc.args)], + [exc[-1], args[-1]]) + self.interface_test_driver(results) + @ignore_deprecation_warnings def test_interface_no_arg(self): # Make sure that with no args that interface is correct exc = Exception() - with warnings.catch_warnings(): - ignore_message_warning() - results = ([len(exc.args), 0], [exc.args, tuple()], - [exc.message, ''], - [str(exc), ''], [unicode(exc), u''], - [repr(exc), exc.__class__.__name__ + '()'], [True, True]) - self.interface_test_driver(results) + results = ([len(exc.args), 0], [exc.args, tuple()], + [exc.message, ''], + [str(exc), ''], [unicode(exc), u''], + [repr(exc), exc.__class__.__name__ + '()'], [True, True]) + self.interface_test_driver(results) def test_message_deprecation(self): @@ -179,6 +186,7 @@ self.fail("TypeError expected when catching %s as specified in a " "tuple" % type(object_)) + @ignore_deprecation_warnings def test_raise_classic(self): # Raising a classic class is okay (for now). class ClassicClass: @@ -194,7 +202,7 @@ except ClassicClass: pass except: - self.fail("unable to raise class class instance") + self.fail("unable to raise classic class instance") def test_raise_new_style_non_exception(self): # You cannot raise a new-style class that does not inherit from Modified: python/branches/release26-maint/Lib/test/test_userstring.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_userstring.py (original) +++ python/branches/release26-maint/Lib/test/test_userstring.py Mon Aug 2 05:14:27 2010 @@ -136,7 +136,10 @@ def test_main(): with warnings.catch_warnings(): - warnings.filterwarnings("ignore", ".*MutableString", + warnings.filterwarnings("ignore", ".*MutableString has been removed", + DeprecationWarning) + warnings.filterwarnings("ignore", + ".*__(get|set|del)slice__ has been removed", DeprecationWarning) test_support.run_unittest(UserStringTest, MutableStringTest) From solipsis at pitrou.net Mon Aug 2 05:22:01 2010 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Mon, 02 Aug 2010 05:22:01 +0200 Subject: [Python-checkins] Daily py3k reference leaks (r83513): sum=0 Message-ID: py3k results for svn r83513 (hg cset ff3294625965) -------------------------------------------------- Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/py3k/refleaks/reflog7P9XBq', '-x'] From python-checkins at python.org Mon Aug 2 06:25:50 2010 From: python-checkins at python.org (ezio.melotti) Date: Mon, 2 Aug 2010 06:25:50 +0200 (CEST) Subject: [Python-checkins] r83519 - python/branches/release26-maint/Lib/test/test_cgi.py Message-ID: <20100802042550.683B6EEC2F@mail.python.org> Author: ezio.melotti Date: Mon Aug 2 06:25:50 2010 New Revision: 83519 Log: Fix incorrect placement of sorted() Modified: python/branches/release26-maint/Lib/test/test_cgi.py Modified: python/branches/release26-maint/Lib/test/test_cgi.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_cgi.py (original) +++ python/branches/release26-maint/Lib/test/test_cgi.py Mon Aug 2 06:25:50 2010 @@ -166,9 +166,9 @@ self.assertEqual(sorted(sd.getlist(key)), sorted(expect_val)) if single_value: self.assertEqual(sorted(sd.values()), - sorted(first_elts(expect.values()))) + first_elts(sorted(expect.values()))) self.assertEqual(sorted(sd.items()), - sorted(first_second_elts(expect.items()))) + first_second_elts(sorted(expect.items()))) def test_weird_formcontentdict(self): # Test the weird FormContentDict classes From python-checkins at python.org Mon Aug 2 07:18:33 2010 From: python-checkins at python.org (ezio.melotti) Date: Mon, 2 Aug 2010 07:18:33 +0200 (CEST) Subject: [Python-checkins] r83520 - python/branches/release26-maint Message-ID: <20100802051833.0E912EEA96@mail.python.org> Author: ezio.melotti Date: Mon Aug 2 07:18:32 2010 New Revision: 83520 Log: Unblocked revisions 77942,79023 via svnmerge ........ r77942 | ezio.melotti | 2010-02-03 07:37:26 +0200 (Wed, 03 Feb 2010) | 1 line #7092: Silence more py3k warnings. Patch by Florent Xicluna. ........ r79023 | ezio.melotti | 2010-03-17 15:52:48 +0200 (Wed, 17 Mar 2010) | 1 line #7092: silence some more py3k warnings. ........ Modified: python/branches/release26-maint/ (props changed) From mal at egenix.com Mon Aug 2 10:42:41 2010 From: mal at egenix.com (M.-A. Lemburg) Date: Mon, 02 Aug 2010 10:42:41 +0200 Subject: [Python-checkins] r83446 - in python/branches/release27-maint: Objects/unicodeobject.c In-Reply-To: <20100801205431.1878EEEB04@mail.python.org> References: <20100801205431.1878EEEB04@mail.python.org> Message-ID: <4C568501.3000201@egenix.com> Hi Georg, georg.brandl wrote: > Author: georg.brandl > Date: Sun Aug 1 22:54:30 2010 > New Revision: 83446 > > Log: > Recorded merge of revisions 83444 via svnmerge from > svn+ssh://pythondev at svn.python.org/python/branches/py3k > > ........ > r83444 | georg.brandl | 2010-08-01 22:51:02 +0200 (So, 01 Aug 2010) | 1 line > > Revert r83395, it introduces test failures and is not necessary anyway since we now have to nul-terminate the string anyway. > ........ > > > Modified: > python/branches/release27-maint/ (props changed) > python/branches/release27-maint/Objects/unicodeobject.c > > Modified: python/branches/release27-maint/Objects/unicodeobject.c > ============================================================================== > --- python/branches/release27-maint/Objects/unicodeobject.c (original) > +++ python/branches/release27-maint/Objects/unicodeobject.c Sun Aug 1 22:54:30 2010 > @@ -294,7 +294,7 @@ > } > > /* We allocate one more byte to make sure the string is > - Ux0000 terminated -- XXX is this needed ? > + Ux0000 terminated; some code relies on that. While that's unfortunately true (e.g. some Windows APIs need this), the code in unicodeobject.c should not rely on this ! Also note that the codec functions work on Py_UNICODE buffers which are *not* guaranteed to be NUL-terminated. > XXX This allocator could further be enhanced by assuring that the > free list never reduces its size below 1. > @@ -3067,7 +3067,7 @@ > > ch2 = *s++; > size--; > - if (ch2 >= 0xDC00 && ch2 <= 0xDFFF && size) { > + if (ch2 >= 0xDC00 && ch2 <= 0xDFFF) { > ucs = (((ch & 0x03FF) << 10) | (ch2 & 0x03FF)) + 0x00010000; > *p++ = '\\'; > *p++ = 'U'; > @@ -3316,7 +3316,7 @@ > > ch2 = *s++; > size--; > - if (ch2 >= 0xDC00 && ch2 <= 0xDFFF && size) { > + if (ch2 >= 0xDC00 && ch2 <= 0xDFFF) { > ucs = (((ch & 0x03FF) << 10) | (ch2 & 0x03FF)) + 0x00010000; > *p++ = '\\'; > *p++ = 'U'; > _______________________________________________ > Python-checkins mailing list > Python-checkins at python.org > http://mail.python.org/mailman/listinfo/python-checkins -- Marc-Andre Lemburg eGenix.com Professional Python Services directly from the Source (#1, Aug 02 2010) >>> Python/Zope Consulting and Support ... http://www.egenix.com/ >>> mxODBC.Zope.Database.Adapter ... http://zope.egenix.com/ >>> mxODBC, mxDateTime, mxTextTools ... http://python.egenix.com/ ________________________________________________________________________ ::: Try our new mxODBC.Connect Python Database Interface for free ! :::: eGenix.com Software, Skills and Services GmbH Pastor-Loeh-Str.48 D-40764 Langenfeld, Germany. CEO Dipl.-Math. Marc-Andre Lemburg Registered at Amtsgericht Duesseldorf: HRB 46611 http://www.egenix.com/company/contact/ From python-checkins at python.org Mon Aug 2 13:04:58 2010 From: python-checkins at python.org (senthil.kumaran) Date: Mon, 2 Aug 2010 13:04:58 +0200 (CEST) Subject: [Python-checkins] r83521 - in python/branches/py3k: Doc/library/http.client.rst Lib/http/client.py Lib/test/test_httplib.py Message-ID: <20100802110458.AF368EE994@mail.python.org> Author: senthil.kumaran Date: Mon Aug 2 13:04:58 2010 New Revision: 83521 Log: Fix Issue8572 - httplib getheader() throws error instead of default Modified: python/branches/py3k/Doc/library/http.client.rst python/branches/py3k/Lib/http/client.py python/branches/py3k/Lib/test/test_httplib.py Modified: python/branches/py3k/Doc/library/http.client.rst ============================================================================== --- python/branches/py3k/Doc/library/http.client.rst (original) +++ python/branches/py3k/Doc/library/http.client.rst Mon Aug 2 13:04:58 2010 @@ -466,7 +466,8 @@ .. method:: HTTPResponse.getheader(name, default=None) Get the contents of the header *name*, or *default* if there is no matching - header. + header. If *default* is an iterator other than a string, then the return + value will be a string consisting of items of the iterator joined by comma. .. method:: HTTPResponse.getheaders() Modified: python/branches/py3k/Lib/http/client.py ============================================================================== --- python/branches/py3k/Lib/http/client.py (original) +++ python/branches/py3k/Lib/http/client.py Mon Aug 2 13:04:58 2010 @@ -602,7 +602,11 @@ def getheader(self, name, default=None): if self.headers is None: raise ResponseNotReady() - return ', '.join(self.headers.get_all(name, default)) + headers = self.headers.get_all(name) or default + if isinstance(headers, str) or not hasattr(headers, '__iter__'): + return headers + else: + return ', '.join(headers) def getheaders(self): """Return list of (header, value) tuples.""" Modified: python/branches/py3k/Lib/test/test_httplib.py ============================================================================== --- python/branches/py3k/Lib/test/test_httplib.py (original) +++ python/branches/py3k/Lib/test/test_httplib.py Mon Aug 2 13:04:58 2010 @@ -442,9 +442,46 @@ self.assertEqual("5", message.get("content-length")) self.assertEqual(b'body\xc1', f.read()) + +class HTTPResponseTest(TestCase): + + def setUp(self): + body = "HTTP/1.1 200 Ok\r\nMy-Header: first-value\r\nMy-Header: \ + second-value\r\n\r\nText" + sock = FakeSocket(body) + self.resp = client.HTTPResponse(sock) + self.resp.begin() + + def test_getting_header(self): + header = self.resp.getheader('My-Header') + self.assertEqual(header, 'first-value, second-value') + + header = self.resp.getheader('My-Header', 'some default') + self.assertEqual(header, 'first-value, second-value') + + def test_getting_nonexistent_header_with_string_default(self): + header = self.resp.getheader('No-Such-Header', 'default-value') + self.assertEqual(header, 'default-value') + + def test_getting_nonexistent_header_with_iterable_default(self): + header = self.resp.getheader('No-Such-Header', ['default', 'values']) + self.assertEqual(header, 'default, values') + + header = self.resp.getheader('No-Such-Header', ('default', 'values')) + self.assertEqual(header, 'default, values') + + def test_getting_nonexistent_header_without_default(self): + header = self.resp.getheader('No-Such-Header') + self.assertEqual(header, None) + + def test_getting_header_defaultint(self): + header = self.resp.getheader('No-Such-Header',default=42) + self.assertEqual(header, 42) + def test_main(verbose=None): support.run_unittest(HeaderTests, OfflineTest, BasicTest, TimeoutTest, - HTTPSTimeoutTest, RequestBodyTest, SourceAddressTest) + HTTPSTimeoutTest, RequestBodyTest, SourceAddressTest, + HTTPResponseTest) if __name__ == '__main__': test_main() From python-checkins at python.org Mon Aug 2 14:01:22 2010 From: python-checkins at python.org (senthil.kumaran) Date: Mon, 2 Aug 2010 14:01:22 +0200 (CEST) Subject: [Python-checkins] r83522 - in python/branches/release31-maint: Doc/library/http.client.rst Lib/http/client.py Lib/test/test_httplib.py Message-ID: <20100802120122.1B066EE9D0@mail.python.org> Author: senthil.kumaran Date: Mon Aug 2 14:01:21 2010 New Revision: 83522 Log: Merged revisions 83521 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83521 | senthil.kumaran | 2010-08-02 16:34:58 +0530 (Mon, 02 Aug 2010) | 3 lines Fix Issue8572 - httplib getheader() throws error instead of default ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Doc/library/http.client.rst python/branches/release31-maint/Lib/http/client.py python/branches/release31-maint/Lib/test/test_httplib.py Modified: python/branches/release31-maint/Doc/library/http.client.rst ============================================================================== --- python/branches/release31-maint/Doc/library/http.client.rst (original) +++ python/branches/release31-maint/Doc/library/http.client.rst Mon Aug 2 14:01:21 2010 @@ -449,7 +449,8 @@ .. method:: HTTPResponse.getheader(name, default=None) Get the contents of the header *name*, or *default* if there is no matching - header. + header. If *default* is an iterator other than a string, then the return + value will be a string consisting of items of the iterator joined by comma. .. method:: HTTPResponse.getheaders() Modified: python/branches/release31-maint/Lib/http/client.py ============================================================================== --- python/branches/release31-maint/Lib/http/client.py (original) +++ python/branches/release31-maint/Lib/http/client.py Mon Aug 2 14:01:21 2010 @@ -602,7 +602,11 @@ def getheader(self, name, default=None): if self.headers is None: raise ResponseNotReady() - return ', '.join(self.headers.get_all(name, default)) + headers = self.headers.get_all(name) or default + if isinstance(headers, str) or not hasattr(headers, '__iter__'): + return headers + else: + return ', '.join(headers) def getheaders(self): """Return list of (header, value) tuples.""" Modified: python/branches/release31-maint/Lib/test/test_httplib.py ============================================================================== --- python/branches/release31-maint/Lib/test/test_httplib.py (original) +++ python/branches/release31-maint/Lib/test/test_httplib.py Mon Aug 2 14:01:21 2010 @@ -405,9 +405,45 @@ self.assertEqual("5", message.get("content-length")) self.assertEqual(b'body\xc1', f.read()) + +class HTTPResponseTest(TestCase): + + def setUp(self): + body = "HTTP/1.1 200 Ok\r\nMy-Header: first-value\r\nMy-Header: \ + second-value\r\n\r\nText" + sock = FakeSocket(body) + self.resp = client.HTTPResponse(sock) + self.resp.begin() + + def test_getting_header(self): + header = self.resp.getheader('My-Header') + self.assertEqual(header, 'first-value, second-value') + + header = self.resp.getheader('My-Header', 'some default') + self.assertEqual(header, 'first-value, second-value') + + def test_getting_nonexistent_header_with_string_default(self): + header = self.resp.getheader('No-Such-Header', 'default-value') + self.assertEqual(header, 'default-value') + + def test_getting_nonexistent_header_with_iterable_default(self): + header = self.resp.getheader('No-Such-Header', ['default', 'values']) + self.assertEqual(header, 'default, values') + + header = self.resp.getheader('No-Such-Header', ('default', 'values')) + self.assertEqual(header, 'default, values') + + def test_getting_nonexistent_header_without_default(self): + header = self.resp.getheader('No-Such-Header') + self.assertEqual(header, None) + + def test_getting_header_defaultint(self): + header = self.resp.getheader('No-Such-Header',default=42) + self.assertEqual(header, 42) + def test_main(verbose=None): support.run_unittest(HeaderTests, OfflineTest, BasicTest, TimeoutTest, - HTTPSTimeoutTest, RequestBodyTest) + HTTPSTimeoutTest, RequestBodyTest, HTTPResponseTest) if __name__ == '__main__': test_main() From python-checkins at python.org Mon Aug 2 14:06:18 2010 From: python-checkins at python.org (georg.brandl) Date: Mon, 2 Aug 2010 14:06:18 +0200 (CEST) Subject: [Python-checkins] r83523 - in python/branches/py3k: Lib/pstats.py Misc/NEWS Message-ID: <20100802120618.BC85FEE9A1@mail.python.org> Author: georg.brandl Date: Mon Aug 2 14:06:18 2010 New Revision: 83523 Log: #9209 and #7781: fix two crashes in pstats interactive browser. Modified: python/branches/py3k/Lib/pstats.py python/branches/py3k/Misc/NEWS Modified: python/branches/py3k/Lib/pstats.py ============================================================================== --- python/branches/py3k/Lib/pstats.py (original) +++ python/branches/py3k/Lib/pstats.py Mon Aug 2 14:06:18 2010 @@ -5,7 +5,7 @@ # Based on prior profile module by Sjoerd Mullender... # which was hacked somewhat by: Guido van Rossum # -# see profile.doc and profile.py for more info. +# see profile.py for more info. # Copyright 1994, by InfoSeek Corporation, all rights reserved. # Written by James Roskind @@ -66,7 +66,7 @@ minor key of 'the name of the function'. Look at the two tables in sort_stats() and get_sort_arg_defs(self) for more examples. - All methods return self, so you can string together commands like: + All methods return self, so you can string together commands like: Stats('foo', 'goo').strip_dirs().sort_stats('calls').\ print_stats(5).print_callers(5) """ @@ -138,7 +138,7 @@ if not arg_list: return self if len(arg_list) > 1: self.add(*arg_list[1:]) other = arg_list[0] - if type(self) != type(other) or self.__class__ != other.__class__: + if type(self) != type(other): other = Stats(other) self.files += other.files self.total_calls += other.total_calls @@ -206,12 +206,12 @@ if not field: self.fcn_list = 0 return self - if len(field) == 1 and type(field[0]) == type(1): + if len(field) == 1 and isinstance(field[0], int): # Be compatible with old profiler field = [ {-1: "stdname", - 0:"calls", - 1:"time", - 2: "cumulative" } [ field[0] ] ] + 0: "calls", + 1: "time", + 2: "cumulative"}[field[0]] ] sort_arg_defs = self.get_sort_arg_defs() sort_tuple = () @@ -288,48 +288,53 @@ def eval_print_amount(self, sel, list, msg): new_list = list - if type(sel) == type(""): + if isinstance(sel, str): + try: + rex = re.compile(sel) + except re.error: + msg += " \n" % sel + return new_list, msg new_list = [] for func in list: - if re.search(sel, func_std_string(func)): + if rex.search(func_std_string(func)): new_list.append(func) else: count = len(list) - if type(sel) == type(1.0) and 0.0 <= sel < 1.0: + if isinstance(sel, float) and 0.0 <= sel < 1.0: count = int(count * sel + .5) new_list = list[:count] - elif type(sel) == type(1) and 0 <= sel < count: + elif isinstance(sel, int) and 0 <= sel < count: count = sel new_list = list[:count] if len(list) != len(new_list): - msg = msg + " List reduced from %r to %r due to restriction <%r>\n" % ( - len(list), len(new_list), sel) + msg += " List reduced from %r to %r due to restriction <%r>\n" % ( + len(list), len(new_list), sel) return new_list, msg def get_print_list(self, sel_list): width = self.max_name_len if self.fcn_list: - list = self.fcn_list[:] + stat_list = self.fcn_list[:] msg = " Ordered by: " + self.sort_type + '\n' else: - list = self.stats.keys() + stat_list = list(self.stats.keys()) msg = " Random listing order was used\n" for selection in sel_list: - list, msg = self.eval_print_amount(selection, list, msg) + stat_list, msg = self.eval_print_amount(selection, stat_list, msg) - count = len(list) + count = len(stat_list) - if not list: - return 0, list + if not stat_list: + return 0, stat_list print(msg, file=self.stream) if count < len(self.stats): width = 0 - for func in list: + for func in stat_list: if len(func_std_string(func)) > width: width = len(func_std_string(func)) - return width+2, list + return width+2, stat_list def print_stats(self, *amount): for filename in self.files: @@ -536,12 +541,10 @@ def __init__(self, profile=None): cmd.Cmd.__init__(self) self.prompt = "% " + self.stats = None + self.stream = sys.stdout if profile is not None: - self.stats = Stats(profile) - self.stream = self.stats.stream - else: - self.stats = None - self.stream = sys.stdout + self.do_read(profile) def generic(self, fn, line): args = line.split() Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Mon Aug 2 14:06:18 2010 @@ -29,6 +29,12 @@ Library ------- +- Issue #7781: Fix restricting stats by entry counts in the pstats + interactive browser. + +- Issue #9209: Do not crash in the pstats interactive browser on invalid + regular expressions. + - Update collections.OrderedDict to match the implementation in Py2.7 (based on lists instead of weakly referenced Link objects). From python-checkins at python.org Mon Aug 2 14:20:23 2010 From: python-checkins at python.org (georg.brandl) Date: Mon, 2 Aug 2010 14:20:23 +0200 (CEST) Subject: [Python-checkins] r83524 - in python/branches/py3k: Lib/cProfile.py Lib/profile.py Misc/NEWS Message-ID: <20100802122023.8C549EE98C@mail.python.org> Author: georg.brandl Date: Mon Aug 2 14:20:23 2010 New Revision: 83524 Log: #9428: fix running scripts from profile/cProfile with their own name and the right namespace. Same fix as for trace.py in #1690103. Modified: python/branches/py3k/Lib/cProfile.py python/branches/py3k/Lib/profile.py python/branches/py3k/Misc/NEWS Modified: python/branches/py3k/Lib/cProfile.py ============================================================================== --- python/branches/py3k/Lib/cProfile.py (original) +++ python/branches/py3k/Lib/cProfile.py Mon Aug 2 14:20:23 2010 @@ -36,7 +36,7 @@ result = prof.print_stats(sort) return result -def runctx(statement, globals, locals, filename=None): +def runctx(statement, globals, locals, filename=None, sort=-1): """Run statement under profiler, supplying your own globals and locals, optionally saving results in filename. @@ -53,7 +53,7 @@ if filename is not None: prof.dump_stats(filename) else: - result = prof.print_stats() + result = prof.print_stats(sort) return result # ____________________________________________________________ @@ -164,7 +164,8 @@ parser.add_option('-o', '--outfile', dest="outfile", help="Save stats to ", default=None) parser.add_option('-s', '--sort', dest="sort", - help="Sort order when printing to stdout, based on pstats.Stats class", default=-1) + help="Sort order when printing to stdout, based on pstats.Stats class", + default=-1) if not sys.argv[1:]: parser.print_usage() @@ -173,14 +174,18 @@ (options, args) = parser.parse_args() sys.argv[:] = args - if (len(sys.argv) > 0): - sys.path.insert(0, os.path.dirname(sys.argv[0])) - fp = open(sys.argv[0]) - try: - script = fp.read() - finally: - fp.close() - run('exec(%r)' % script, options.outfile, options.sort) + if len(args) > 0: + progname = args[0] + sys.path.insert(0, os.path.dirname(progname)) + with open(progname, 'rb') as fp: + code = compile(fp.read(), progname, 'exec') + globs = { + '__file__': progname, + '__name__': '__main__', + '__package__': None, + '__cached__': None, + } + runctx(code, globs, None, options.outfile, options.sort) else: parser.print_usage() return parser Modified: python/branches/py3k/Lib/profile.py ============================================================================== --- python/branches/py3k/Lib/profile.py (original) +++ python/branches/py3k/Lib/profile.py Mon Aug 2 14:20:23 2010 @@ -75,7 +75,7 @@ else: return prof.print_stats(sort) -def runctx(statement, globals, locals, filename=None): +def runctx(statement, globals, locals, filename=None, sort=-1): """Run statement under profiler, supplying your own globals and locals, optionally saving results in filename. @@ -90,7 +90,7 @@ if filename is not None: prof.dump_stats(filename) else: - return prof.print_stats() + return prof.print_stats(sort) if hasattr(os, "times"): def _get_time_times(timer=os.times): @@ -582,20 +582,28 @@ parser.add_option('-o', '--outfile', dest="outfile", help="Save stats to ", default=None) parser.add_option('-s', '--sort', dest="sort", - help="Sort order when printing to stdout, based on pstats.Stats class", default=-1) + help="Sort order when printing to stdout, based on pstats.Stats class", + default=-1) if not sys.argv[1:]: parser.print_usage() sys.exit(2) (options, args) = parser.parse_args() + sys.argv[:] = args - if (len(args) > 0): - sys.argv[:] = args - sys.path.insert(0, os.path.dirname(sys.argv[0])) - with open(sys.argv[0], 'rb') as fp: - script = fp.read() - run('exec(%r)' % script, options.outfile, options.sort) + if len(args) > 0: + progname = args[0] + sys.path.insert(0, os.path.dirname(progname)) + with open(progname, 'rb') as fp: + code = compile(fp.read(), progname, 'exec') + globs = { + '__file__': progname, + '__name__': '__main__', + '__package__': None, + '__cached__': None, + } + runctx(code, globs, None, options.outfile, options.sort) else: parser.print_usage() return parser Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Mon Aug 2 14:20:23 2010 @@ -29,6 +29,9 @@ Library ------- +- Issue #9428: Fix running scripts with the profile/cProfile modules from + the command line. + - Issue #7781: Fix restricting stats by entry counts in the pstats interactive browser. From python-checkins at python.org Mon Aug 2 14:36:25 2010 From: python-checkins at python.org (georg.brandl) Date: Mon, 2 Aug 2010 14:36:25 +0200 (CEST) Subject: [Python-checkins] r83525 - python/branches/py3k/Lib/trace.py Message-ID: <20100802123625.21F2CEE9E0@mail.python.org> Author: georg.brandl Date: Mon Aug 2 14:36:24 2010 New Revision: 83525 Log: Get rid of spurious "threading" entries in trace output. Modified: python/branches/py3k/Lib/trace.py Modified: python/branches/py3k/Lib/trace.py ============================================================================== --- python/branches/py3k/Lib/trace.py (original) +++ python/branches/py3k/Lib/trace.py Mon Aug 2 14:36:24 2010 @@ -493,8 +493,8 @@ import __main__ dict = __main__.__dict__ if not self.donothing: - sys.settrace(self.globaltrace) threading.settrace(self.globaltrace) + sys.settrace(self.globaltrace) try: exec(cmd, dict, dict) finally: @@ -506,8 +506,8 @@ if globals is None: globals = {} if locals is None: locals = {} if not self.donothing: - sys.settrace(self.globaltrace) threading.settrace(self.globaltrace) + sys.settrace(self.globaltrace) try: exec(cmd, globals, locals) finally: From python-checkins at python.org Mon Aug 2 14:40:22 2010 From: python-checkins at python.org (georg.brandl) Date: Mon, 2 Aug 2010 14:40:22 +0200 (CEST) Subject: [Python-checkins] r83526 - python/branches/py3k/Lib/trace.py Message-ID: <20100802124022.C16C2EE9E0@mail.python.org> Author: georg.brandl Date: Mon Aug 2 14:40:22 2010 New Revision: 83526 Log: Fix softspace relic. Modified: python/branches/py3k/Lib/trace.py Modified: python/branches/py3k/Lib/trace.py ============================================================================== --- python/branches/py3k/Lib/trace.py (original) +++ python/branches/py3k/Lib/trace.py Mon Aug 2 14:40:22 2010 @@ -626,7 +626,7 @@ print('%.2f' % (time.time() - self.start_time), end=' ') bname = os.path.basename(filename) print("%s(%d): %s" % (bname, lineno, - linecache.getline(filename, lineno)), end=' ') + linecache.getline(filename, lineno)), end='') return self.localtrace def localtrace_trace(self, frame, why, arg): @@ -639,7 +639,7 @@ print('%.2f' % (time.time() - self.start_time), end=' ') bname = os.path.basename(filename) print("%s(%d): %s" % (bname, lineno, - linecache.getline(filename, lineno)), end=' ') + linecache.getline(filename, lineno)), end='') return self.localtrace def localtrace_count(self, frame, why, arg): From python-checkins at python.org Mon Aug 2 14:48:47 2010 From: python-checkins at python.org (georg.brandl) Date: Mon, 2 Aug 2010 14:48:47 +0200 (CEST) Subject: [Python-checkins] r83527 - python/branches/py3k/Lib/test/test_trace.py Message-ID: <20100802124847.0C868EE9FA@mail.python.org> Author: georg.brandl Date: Mon Aug 2 14:48:46 2010 New Revision: 83527 Log: #3821: beginnings of a trace.py unittest. Added: python/branches/py3k/Lib/test/test_trace.py (contents, props changed) Added: python/branches/py3k/Lib/test/test_trace.py ============================================================================== --- (empty file) +++ python/branches/py3k/Lib/test/test_trace.py Mon Aug 2 14:48:46 2010 @@ -0,0 +1,47 @@ +# Testing the trace module + +from test.support import run_unittest, TESTFN, rmtree, unlink, captured_stdout +import unittest +import trace +import os, sys + +class TestCoverage(unittest.TestCase): + def tearDown(self): + rmtree(TESTFN) + unlink(TESTFN) + + def _coverage(self, tracer): + tracer.run('from test import test_pprint; test_pprint.test_main()') + r = tracer.results() + r.write_results(show_missing=True, summary=True, coverdir=TESTFN) + + def test_coverage(self): + tracer = trace.Trace(trace=0, count=1) + with captured_stdout() as stdout: + self._coverage(tracer) + stdout = stdout.getvalue() + self.assertTrue("pprint.py" in stdout) + self.assertTrue("case.py" in stdout) # from unittest + files = os.listdir(TESTFN) + self.assertTrue("pprint.cover" in files) + self.assertTrue("unittest.case.cover" in files) + + def test_coverage_ignore(self): + # Ignore all files, nothing should be traced nor printed + libpath = os.path.normpath(os.path.dirname(os.__file__)) + # sys.prefix does not work when running from a checkout + tracer = trace.Trace(ignoredirs=[sys.prefix, sys.exec_prefix, libpath], + trace=0, count=1) + with captured_stdout() as stdout: + self._coverage(tracer) + self.assertEquals(stdout.getvalue(), "") + if os.path.exists(TESTFN): + files = os.listdir(TESTFN) + self.assertEquals(files, []) + + +def test_main(): + run_unittest(__name__) + +if __name__ == "__main__": + test_main() From python-checkins at python.org Mon Aug 2 14:54:24 2010 From: python-checkins at python.org (georg.brandl) Date: Mon, 2 Aug 2010 14:54:24 +0200 (CEST) Subject: [Python-checkins] r83528 - python/branches/py3k/Doc/documenting/markup.rst Message-ID: <20100802125424.B0D6EEE9FA@mail.python.org> Author: georg.brandl Date: Mon Aug 2 14:54:24 2010 New Revision: 83528 Log: Document how to refer to decorators and decorator methods. Modified: python/branches/py3k/Doc/documenting/markup.rst Modified: python/branches/py3k/Doc/documenting/markup.rst ============================================================================== --- python/branches/py3k/Doc/documenting/markup.rst (original) +++ python/branches/py3k/Doc/documenting/markup.rst Mon Aug 2 14:54:24 2010 @@ -205,6 +205,9 @@ Set name of the decorated function to *name*. + There is no ``deco`` role to link to a decorator that is marked up with + this directive; rather, use the ``:func:`` role. + .. describe:: class Describes a class. The signature can include parentheses with parameters @@ -226,6 +229,8 @@ Same as ``decorator``, but for decorators that are methods. + Refer to a decorator method using the ``:meth:`` role. + .. describe:: opcode Describes a Python :term:`bytecode` instruction. From barry at python.org Mon Aug 2 17:08:10 2010 From: barry at python.org (Barry Warsaw) Date: Mon, 2 Aug 2010 11:08:10 -0400 Subject: [Python-checkins] python-checkins Reply-To now set for python-dev Message-ID: <20100802110810.06d2b9fd@heresy> Over in #python-dev, Georg reminded us of a change to the python-checkins mailing list that was discussed a few weeks ago: http://mail.python.org/pipermail/python-dev/2010-July/101853.html Despite the mild preference of redirecting python-checkins to python-committers, I noticed that the list was already set up to redirect to python-dev (but the Reply-To munging was disabled). I've now re-enabled redirects of python-checkins to python-dev. I think it's better to default to more openness and it's not really that much traffic anyway. If it gets obnoxious we can narrow things, but let's see how it goes. -Barry -------------- next part -------------- A non-text attachment was scrubbed... Name: signature.asc Type: application/pgp-signature Size: 836 bytes Desc: not available URL: From python-checkins at python.org Mon Aug 2 19:09:02 2010 From: python-checkins at python.org (senthil.kumaran) Date: Mon, 2 Aug 2010 19:09:02 +0200 (CEST) Subject: [Python-checkins] r83529 - python/branches/py3k/Doc/library/http.client.rst Message-ID: <20100802170902.81C45EEA5F@mail.python.org> Author: senthil.kumaran Date: Mon Aug 2 19:09:02 2010 New Revision: 83529 Log: Rewording the getheader method of HTTPResponse. Modified: python/branches/py3k/Doc/library/http.client.rst Modified: python/branches/py3k/Doc/library/http.client.rst ============================================================================== --- python/branches/py3k/Doc/library/http.client.rst (original) +++ python/branches/py3k/Doc/library/http.client.rst Mon Aug 2 19:09:02 2010 @@ -465,9 +465,10 @@ .. method:: HTTPResponse.getheader(name, default=None) - Get the contents of the header *name*, or *default* if there is no matching - header. If *default* is an iterator other than a string, then the return - value will be a string consisting of items of the iterator joined by comma. + Return the value of the header *name*, or *default* if there is no header + matching *name*. If there is more than one header with the name *name*, + return all of the values joined by ', '. If 'default' is any iterable other + than a single string, its elements are similarly returned joined by commas. .. method:: HTTPResponse.getheaders() From python-checkins at python.org Mon Aug 2 19:11:51 2010 From: python-checkins at python.org (senthil.kumaran) Date: Mon, 2 Aug 2010 19:11:51 +0200 (CEST) Subject: [Python-checkins] r83530 - in python/branches/release31-maint: Doc/library/http.client.rst Message-ID: <20100802171151.15957EEB78@mail.python.org> Author: senthil.kumaran Date: Mon Aug 2 19:11:50 2010 New Revision: 83530 Log: Merged revisions 83529 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83529 | senthil.kumaran | 2010-08-02 22:39:02 +0530 (Mon, 02 Aug 2010) | 3 lines Rewording the getheader method of HTTPResponse. ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Doc/library/http.client.rst Modified: python/branches/release31-maint/Doc/library/http.client.rst ============================================================================== --- python/branches/release31-maint/Doc/library/http.client.rst (original) +++ python/branches/release31-maint/Doc/library/http.client.rst Mon Aug 2 19:11:50 2010 @@ -448,9 +448,10 @@ .. method:: HTTPResponse.getheader(name, default=None) - Get the contents of the header *name*, or *default* if there is no matching - header. If *default* is an iterator other than a string, then the return - value will be a string consisting of items of the iterator joined by comma. + Return the value of the header *name*, or *default* if there is no header + matching *name*. If there is more than one header with the name *name*, + return all of the values joined by ', '. If 'default' is any iterable other + than a single string, its elements are similarly returned joined by commas. .. method:: HTTPResponse.getheaders() From python-checkins at python.org Mon Aug 2 19:24:49 2010 From: python-checkins at python.org (georg.brandl) Date: Mon, 2 Aug 2010 19:24:49 +0200 (CEST) Subject: [Python-checkins] r83531 - in python/branches/py3k: Lib/pstats.py Lib/test/test_pstats.py Misc/NEWS Message-ID: <20100802172449.7CCE1EE9B2@mail.python.org> Author: georg.brandl Date: Mon Aug 2 19:24:49 2010 New Revision: 83531 Log: #7372: fix regression in pstats: a previous fix to handle cProfile data in add_callers broke handling of profile data. Modified: python/branches/py3k/Lib/pstats.py python/branches/py3k/Lib/test/test_pstats.py python/branches/py3k/Misc/NEWS Modified: python/branches/py3k/Lib/pstats.py ============================================================================== --- python/branches/py3k/Lib/pstats.py (original) +++ python/branches/py3k/Lib/pstats.py Mon Aug 2 19:24:49 2010 @@ -506,8 +506,13 @@ new_callers[func] = caller for func, caller in source.items(): if func in new_callers: - new_callers[func] = tuple([i[0] + i[1] for i in - zip(caller, new_callers[func])]) + if isinstance(caller, tuple): + # format used by cProfile + new_callers[func] = tuple([i[0] + i[1] for i in + zip(caller, new_callers[func])]) + else: + # format used by profile + new_callers[func] += caller else: new_callers[func] = caller return new_callers Modified: python/branches/py3k/Lib/test/test_pstats.py ============================================================================== --- python/branches/py3k/Lib/test/test_pstats.py (original) +++ python/branches/py3k/Lib/test/test_pstats.py Mon Aug 2 19:24:49 2010 @@ -10,10 +10,16 @@ def test_combine_results(self): """pstats.add_callers should combine the call results of both target and source by adding the call time. See issue1269.""" + # new format: used by the cProfile module target = {"a": (1, 2, 3, 4)} source = {"a": (1, 2, 3, 4), "b": (5, 6, 7, 8)} new_callers = pstats.add_callers(target, source) self.assertEqual(new_callers, {'a': (2, 4, 6, 8), 'b': (5, 6, 7, 8)}) + # old format: used by the profile module + target = {"a": 1} + source = {"a": 1, "b": 5} + new_callers = pstats.add_callers(target, source) + self.assertEqual(new_callers, {'a': 2, 'b': 5}) def test_main(): Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Mon Aug 2 19:24:49 2010 @@ -29,6 +29,9 @@ Library ------- +- Issue #7372: Fix pstats regression when stripping paths from profile + data generated with the profile module. + - Issue #9428: Fix running scripts with the profile/cProfile modules from the command line. From python-checkins at python.org Mon Aug 2 19:31:33 2010 From: python-checkins at python.org (ezio.melotti) Date: Mon, 2 Aug 2010 19:31:33 +0200 (CEST) Subject: [Python-checkins] r83532 - python/branches/release26-maint Message-ID: <20100802173133.DEAC9EEB9C@mail.python.org> Author: ezio.melotti Date: Mon Aug 2 19:31:33 2010 New Revision: 83532 Log: Unblocked revisions 78758 via svnmerge ........ r78758 | florent.xicluna | 2010-03-07 14:18:33 +0200 (Sun, 07 Mar 2010) | 4 lines Issue #7849: Now the utility ``check_warnings`` verifies if the warnings are effectively raised. A new utility ``check_py3k_warnings`` deals with py3k warnings. ........ Modified: python/branches/release26-maint/ (props changed) From python-checkins at python.org Mon Aug 2 19:34:59 2010 From: python-checkins at python.org (georg.brandl) Date: Mon, 2 Aug 2010 19:34:59 +0200 (CEST) Subject: [Python-checkins] r83533 - in python/branches/release27-maint: Lib/pstats.py Lib/test/test_pstats.py Misc/NEWS Message-ID: <20100802173459.164C4EF0E@mail.python.org> Author: georg.brandl Date: Mon Aug 2 19:34:58 2010 New Revision: 83533 Log: Merged revisions 83531 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83531 | georg.brandl | 2010-08-02 19:24:49 +0200 (Mo, 02 Aug 2010) | 1 line #7372: fix regression in pstats: a previous fix to handle cProfile data in add_callers broke handling of profile data. ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Lib/pstats.py python/branches/release27-maint/Lib/test/test_pstats.py python/branches/release27-maint/Misc/NEWS Modified: python/branches/release27-maint/Lib/pstats.py ============================================================================== --- python/branches/release27-maint/Lib/pstats.py (original) +++ python/branches/release27-maint/Lib/pstats.py Mon Aug 2 19:34:58 2010 @@ -513,8 +513,13 @@ new_callers[func] = caller for func, caller in source.iteritems(): if func in new_callers: - new_callers[func] = tuple([i[0] + i[1] for i in - zip(caller, new_callers[func])]) + if isinstance(caller, tuple): + # format used by cProfile + new_callers[func] = tuple([i[0] + i[1] for i in + zip(caller, new_callers[func])]) + else: + # format used by profile + new_callers[func] += caller else: new_callers[func] = caller return new_callers Modified: python/branches/release27-maint/Lib/test/test_pstats.py ============================================================================== --- python/branches/release27-maint/Lib/test/test_pstats.py (original) +++ python/branches/release27-maint/Lib/test/test_pstats.py Mon Aug 2 19:34:58 2010 @@ -10,10 +10,16 @@ def test_combine_results(self): """pstats.add_callers should combine the call results of both target and source by adding the call time. See issue1269.""" + # new format: used by the cProfile module target = {"a": (1, 2, 3, 4)} source = {"a": (1, 2, 3, 4), "b": (5, 6, 7, 8)} new_callers = pstats.add_callers(target, source) self.assertEqual(new_callers, {'a': (2, 4, 6, 8), 'b': (5, 6, 7, 8)}) + # old format: used by the profile module + target = {"a": 1} + source = {"a": 1, "b": 5} + new_callers = pstats.add_callers(target, source) + self.assertEqual(new_callers, {'a': 2, 'b': 5}) def test_main(): Modified: python/branches/release27-maint/Misc/NEWS ============================================================================== --- python/branches/release27-maint/Misc/NEWS (original) +++ python/branches/release27-maint/Misc/NEWS Mon Aug 2 19:34:58 2010 @@ -26,6 +26,9 @@ - Issue #9354: Provide getsockopt() in asyncore's file_wrapper. +- Issue #7372: Fix pstats regression when stripping paths from profile + data generated with the profile module. + - Issue #4108: In urllib.robotparser, if there are multiple 'User-agent: *' entries, consider the first one. From python-checkins at python.org Mon Aug 2 19:36:05 2010 From: python-checkins at python.org (georg.brandl) Date: Mon, 2 Aug 2010 19:36:05 +0200 (CEST) Subject: [Python-checkins] r83534 - in python/branches/release31-maint: Lib/pstats.py Lib/test/test_pstats.py Misc/NEWS Message-ID: <20100802173605.9C5DAEEBBE@mail.python.org> Author: georg.brandl Date: Mon Aug 2 19:36:05 2010 New Revision: 83534 Log: Merged revisions 83531 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83531 | georg.brandl | 2010-08-02 19:24:49 +0200 (Mo, 02 Aug 2010) | 1 line #7372: fix regression in pstats: a previous fix to handle cProfile data in add_callers broke handling of profile data. ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Lib/pstats.py python/branches/release31-maint/Lib/test/test_pstats.py python/branches/release31-maint/Misc/NEWS Modified: python/branches/release31-maint/Lib/pstats.py ============================================================================== --- python/branches/release31-maint/Lib/pstats.py (original) +++ python/branches/release31-maint/Lib/pstats.py Mon Aug 2 19:36:05 2010 @@ -521,8 +521,13 @@ new_callers[func] = caller for func, caller in source.items(): if func in new_callers: - new_callers[func] = tuple([i[0] + i[1] for i in - zip(caller, new_callers[func])]) + if isinstance(caller, tuple): + # format used by cProfile + new_callers[func] = tuple([i[0] + i[1] for i in + zip(caller, new_callers[func])]) + else: + # format used by profile + new_callers[func] += caller else: new_callers[func] = caller return new_callers Modified: python/branches/release31-maint/Lib/test/test_pstats.py ============================================================================== --- python/branches/release31-maint/Lib/test/test_pstats.py (original) +++ python/branches/release31-maint/Lib/test/test_pstats.py Mon Aug 2 19:36:05 2010 @@ -10,10 +10,16 @@ def test_combine_results(self): """pstats.add_callers should combine the call results of both target and source by adding the call time. See issue1269.""" + # new format: used by the cProfile module target = {"a": (1, 2, 3, 4)} source = {"a": (1, 2, 3, 4), "b": (5, 6, 7, 8)} new_callers = pstats.add_callers(target, source) self.assertEqual(new_callers, {'a': (2, 4, 6, 8), 'b': (5, 6, 7, 8)}) + # old format: used by the profile module + target = {"a": 1} + source = {"a": 1, "b": 5} + new_callers = pstats.add_callers(target, source) + self.assertEqual(new_callers, {'a': 2, 'b': 5}) def test_main(): Modified: python/branches/release31-maint/Misc/NEWS ============================================================================== --- python/branches/release31-maint/Misc/NEWS (original) +++ python/branches/release31-maint/Misc/NEWS Mon Aug 2 19:36:05 2010 @@ -84,6 +84,9 @@ Library ------- +- Issue #7372: Fix pstats regression when stripping paths from profile + data generated with the profile module. + - Issue #4108: In urllib.robotparser, if there are multiple 'User-agent: *' entries, consider the first one. From python-checkins at python.org Mon Aug 2 19:40:28 2010 From: python-checkins at python.org (georg.brandl) Date: Mon, 2 Aug 2010 19:40:28 +0200 (CEST) Subject: [Python-checkins] r83535 - in python/branches/release26-maint: Lib/pstats.py Lib/test/test_pstats.py Misc/NEWS Message-ID: <20100802174028.53D8FEEBAE@mail.python.org> Author: georg.brandl Date: Mon Aug 2 19:40:28 2010 New Revision: 83535 Log: Merged revisions 83533 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/release27-maint ................ r83533 | georg.brandl | 2010-08-02 19:34:58 +0200 (Mo, 02 Aug 2010) | 9 lines Merged revisions 83531 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83531 | georg.brandl | 2010-08-02 19:24:49 +0200 (Mo, 02 Aug 2010) | 1 line #7372: fix regression in pstats: a previous fix to handle cProfile data in add_callers broke handling of profile data. ........ ................ Modified: python/branches/release26-maint/ (props changed) python/branches/release26-maint/Lib/pstats.py python/branches/release26-maint/Lib/test/test_pstats.py python/branches/release26-maint/Misc/NEWS Modified: python/branches/release26-maint/Lib/pstats.py ============================================================================== --- python/branches/release26-maint/Lib/pstats.py (original) +++ python/branches/release26-maint/Lib/pstats.py Mon Aug 2 19:40:28 2010 @@ -522,8 +522,13 @@ new_callers[func] = caller for func, caller in source.iteritems(): if func in new_callers: - new_callers[func] = tuple([i[0] + i[1] for i in - zip(caller, new_callers[func])]) + if isinstance(caller, tuple): + # format used by cProfile + new_callers[func] = tuple([i[0] + i[1] for i in + zip(caller, new_callers[func])]) + else: + # format used by profile + new_callers[func] += caller else: new_callers[func] = caller return new_callers Modified: python/branches/release26-maint/Lib/test/test_pstats.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_pstats.py (original) +++ python/branches/release26-maint/Lib/test/test_pstats.py Mon Aug 2 19:40:28 2010 @@ -10,10 +10,16 @@ def test_combine_results(self): """pstats.add_callers should combine the call results of both target and source by adding the call time. See issue1269.""" + # new format: used by the cProfile module target = {"a": (1, 2, 3, 4)} source = {"a": (1, 2, 3, 4), "b": (5, 6, 7, 8)} new_callers = pstats.add_callers(target, source) self.assertEqual(new_callers, {'a': (2, 4, 6, 8), 'b': (5, 6, 7, 8)}) + # old format: used by the profile module + target = {"a": 1} + source = {"a": 1, "b": 5} + new_callers = pstats.add_callers(target, source) + self.assertEqual(new_callers, {'a': 2, 'b': 5}) def test_main(): Modified: python/branches/release26-maint/Misc/NEWS ============================================================================== --- python/branches/release26-maint/Misc/NEWS (original) +++ python/branches/release26-maint/Misc/NEWS Mon Aug 2 19:40:28 2010 @@ -35,6 +35,9 @@ when turned into an exception: in this case the exception simply gets ignored. +- Issue #7372: Fix pstats regression when stripping paths from profile + data generated with the profile module. + - Issue #4108: In urllib.robotparser, if there are multiple 'User-agent: *' entries, consider the first one. From python-checkins at python.org Mon Aug 2 19:49:25 2010 From: python-checkins at python.org (georg.brandl) Date: Mon, 2 Aug 2010 19:49:25 +0200 (CEST) Subject: [Python-checkins] r83536 - python/branches/py3k/Doc/c-api/weakref.rst Message-ID: <20100802174925.6B4C6EE9BB@mail.python.org> Author: georg.brandl Date: Mon Aug 2 19:49:25 2010 New Revision: 83536 Log: #8578: mention danger of not incref'ing weak referenced object. Modified: python/branches/py3k/Doc/c-api/weakref.rst Modified: python/branches/py3k/Doc/c-api/weakref.rst ============================================================================== --- python/branches/py3k/Doc/c-api/weakref.rst (original) +++ python/branches/py3k/Doc/c-api/weakref.rst Mon Aug 2 19:49:25 2010 @@ -53,7 +53,14 @@ .. cfunction:: PyObject* PyWeakref_GetObject(PyObject *ref) Return the referenced object from a weak reference, *ref*. If the referent is - no longer live, returns ``None``. + no longer live, returns :const:`Py_None`. + + .. warning:: + + This function returns a **borrowed reference** to the referenced object. + This means that you should always call :cfunc:`Py_INCREF` on the object + except if you know that it cannot be destroyed while you are still + using it. .. cfunction:: PyObject* PyWeakref_GET_OBJECT(PyObject *ref) From python-checkins at python.org Mon Aug 2 20:10:10 2010 From: python-checkins at python.org (ezio.melotti) Date: Mon, 2 Aug 2010 20:10:10 +0200 (CEST) Subject: [Python-checkins] r83537 - in python/branches/release26-maint: Lib/test/string_tests.py Lib/test/test_fileio.py Lib/test/test_index.py Lib/test/test_random.py Lib/test/test_support.py Lib/test/test_unicode.py Misc/NEWS Message-ID: <20100802181010.187BAEEA40@mail.python.org> Author: ezio.melotti Date: Mon Aug 2 20:10:09 2010 New Revision: 83537 Log: Merged revisions 78758 via svnmerge from svn+ssh://pythondev at svn.python.org/python/trunk ........ r78758 | florent.xicluna | 2010-03-07 14:18:33 +0200 (Sun, 07 Mar 2010) | 4 lines Issue #7849: Now the utility ``check_warnings`` verifies if the warnings are effectively raised. A new utility ``check_py3k_warnings`` deals with py3k warnings. ........ Modified: python/branches/release26-maint/ (props changed) python/branches/release26-maint/Lib/test/string_tests.py python/branches/release26-maint/Lib/test/test_fileio.py python/branches/release26-maint/Lib/test/test_index.py python/branches/release26-maint/Lib/test/test_random.py python/branches/release26-maint/Lib/test/test_support.py python/branches/release26-maint/Lib/test/test_unicode.py python/branches/release26-maint/Misc/NEWS Modified: python/branches/release26-maint/Lib/test/string_tests.py ============================================================================== --- python/branches/release26-maint/Lib/test/string_tests.py (original) +++ python/branches/release26-maint/Lib/test/string_tests.py Mon Aug 2 20:10:09 2010 @@ -686,8 +686,9 @@ EQ("bobobXbobob", "bobobobXbobobob", "replace", "bobob", "bob") EQ("BOBOBOB", "BOBOBOB", "replace", "bob", "bobby") - ba = buffer('a') - bb = buffer('b') + with test_support._check_py3k_warnings(): + ba = buffer('a') + bb = buffer('b') EQ("bbc", "abc", "replace", ba, bb) EQ("aac", "abc", "replace", bb, ba) Modified: python/branches/release26-maint/Lib/test/test_fileio.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_fileio.py (original) +++ python/branches/release26-maint/Lib/test/test_fileio.py Mon Aug 2 20:10:09 2010 @@ -259,7 +259,7 @@ self.assertRaises(TypeError, _fileio._FileIO, "1", 0, 0) def testWarnings(self): - with check_warnings() as w: + with check_warnings(quiet=True) as w: self.assertEqual(w.warnings, []) self.assertRaises(TypeError, _fileio._FileIO, []) self.assertEqual(w.warnings, []) Modified: python/branches/release26-maint/Lib/test/test_index.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_index.py (original) +++ python/branches/release26-maint/Lib/test/test_index.py Mon Aug 2 20:10:09 2010 @@ -195,8 +195,9 @@ x = GetItem() self.assertEqual(x[self.pos], self.pos) self.assertEqual(x[self.neg], self.neg) - self.assertEqual(x[self.neg:self.pos], (maxint+minsize, maxsize)) - self.assertEqual(x[self.neg:self.pos:1].indices(maxsize), (0, maxsize, 1)) + with test_support._check_py3k_warnings(): + self.assertEqual(x[self.neg:self.pos], (maxint+minsize, maxsize)) + self.assertEqual(x[self.neg:self.pos:1].indices(maxsize), (0, maxsize, 1)) def test_getitem(self): self._getitem_helper(object) Modified: python/branches/release26-maint/Lib/test/test_random.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_random.py (original) +++ python/branches/release26-maint/Lib/test/test_random.py Mon Aug 2 20:10:09 2010 @@ -52,10 +52,11 @@ state3 = self.gen.getstate() # s/b distinct from state2 self.assertNotEqual(state2, state3) - self.assertRaises(TypeError, self.gen.jumpahead) # needs an arg - self.assertRaises(TypeError, self.gen.jumpahead, "ick") # wrong type - self.assertRaises(TypeError, self.gen.jumpahead, 2.3) # wrong type - self.assertRaises(TypeError, self.gen.jumpahead, 2, 3) # too many + with test_support._check_py3k_warnings(quiet=True): + self.assertRaises(TypeError, self.gen.jumpahead) # needs an arg + self.assertRaises(TypeError, self.gen.jumpahead, "ick") # wrong type + self.assertRaises(TypeError, self.gen.jumpahead, 2.3) # wrong type + self.assertRaises(TypeError, self.gen.jumpahead, 2, 3) # too many def test_sample(self): # For the entire allowable range of 0 <= k <= N, validate that Modified: python/branches/release26-maint/Lib/test/test_support.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_support.py (original) +++ python/branches/release26-maint/Lib/test/test_support.py Mon Aug 2 20:10:09 2010 @@ -11,6 +11,7 @@ import shutil import warnings import unittest +import re __all__ = ["Error", "TestFailed", "TestSkipped", "ResourceDenied", "import_module", "verbose", "use_resources", "max_memuse", "record_original_stdout", @@ -18,8 +19,8 @@ "is_resource_enabled", "requires", "find_unused_port", "bind_port", "fcmp", "have_unicode", "is_jython", "TESTFN", "HOST", "FUZZ", "findfile", "verify", "vereq", "sortdict", "check_syntax_error", - "open_urlresource", "check_warnings", "CleanImport", - "EnvironmentVarGuard", "captured_output", + "open_urlresource", "check_warnings", "_check_py3k_warnings", + "CleanImport", "EnvironmentVarGuard", "captured_output", "captured_stdout", "TransientResource", "transient_internet", "run_with_locale", "set_memlimit", "bigmemtest", "bigaddrspacetest", "BasicTestRunner", "run_unittest", "run_doctest", "threading_setup", @@ -406,22 +407,103 @@ entry to the warnings.catch_warnings() context manager. """ def __init__(self, warnings_list): - self.warnings = warnings_list + self._warnings = warnings_list + self._last = 0 def __getattr__(self, attr): - if self.warnings: - return getattr(self.warnings[-1], attr) + if len(self._warnings) > self._last: + return getattr(self._warnings[-1], attr) elif attr in warnings.WarningMessage._WARNING_DETAILS: return None raise AttributeError("%r has no attribute %r" % (self, attr)) + @property + def warnings(self): + return self._warnings[self._last:] + def reset(self): - del self.warnings[:] + self._last = len(self._warnings) - at contextlib.contextmanager -def check_warnings(): + +def _filterwarnings(filters, quiet=False): + """Catch the warnings, then check if all the expected + warnings have been raised and re-raise unexpected warnings. + If 'quiet' is True, only re-raise the unexpected warnings. + """ + # Clear the warning registry of the calling module + # in order to re-raise the warnings. + frame = sys._getframe(2) + registry = frame.f_globals.get('__warningregistry__') + if registry: + registry.clear() with warnings.catch_warnings(record=True) as w: + # Disable filters, to record all warnings. Because + # test_warnings swap the module, we need to look up + # in the sys.modules dictionary. + sys.modules['warnings'].resetwarnings() yield WarningsRecorder(w) + # Filter the recorded warnings + reraise = [warning.message for warning in w] + missing = [] + for msg, cat in filters: + seen = False + for exc in reraise[:]: + message = str(exc) + # Filter out the matching messages + if (re.match(msg, message, re.I) and + issubclass(exc.__class__, cat)): + seen = True + reraise.remove(exc) + if not seen and not quiet: + # This filter caught nothing + missing.append((msg, cat.__name__)) + for exc in reraise: + raise AssertionError("unhandled warning %r" % exc) + for filter in missing: + raise AssertionError("filter (%r, %s) did not caught any warning" % + filter) + + + at contextlib.contextmanager +def check_warnings(*filters, **kwargs): + """Context manager to silence warnings. + + Accept 2-tuples as positional arguments: + ("message regexp", WarningCategory) + + Optional argument: + - if 'quiet' is True, it does not fail if a filter catches nothing + (default False) + + Without argument, it defaults to: + check_warnings(("", Warning), quiet=False) + """ + if not filters: + filters = (("", Warning),) + return _filterwarnings(filters, kwargs.get('quiet')) + + + at contextlib.contextmanager +def _check_py3k_warnings(*filters, **kwargs): + """Context manager to silence py3k warnings. + + Accept 2-tuples as positional arguments: + ("message regexp", WarningCategory) + + Optional argument: + - if 'quiet' is True, it does not fail if a filter catches nothing + (default False) + + Without argument, it defaults to: + _check_py3k_warnings(("", DeprecationWarning), quiet=False) + """ + if sys.py3kwarning: + if not filters: + filters = (("", DeprecationWarning),) + else: + # It should not raise any py3k warning + filters = () + return _filterwarnings(filters, kwargs.get('quiet')) class CleanImport(object): @@ -595,7 +677,6 @@ MAX_Py_ssize_t = sys.maxsize def set_memlimit(limit): - import re global max_memuse global real_max_memuse sizes = { Modified: python/branches/release26-maint/Lib/test/test_unicode.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_unicode.py (original) +++ python/branches/release26-maint/Lib/test/test_unicode.py Mon Aug 2 20:10:09 2010 @@ -511,9 +511,11 @@ ) if not sys.platform.startswith('java'): + with test_support._check_py3k_warnings(): + buf = buffer('character buffers are decoded to unicode') self.assertEqual( unicode( - buffer('character buffers are decoded to unicode'), + buf, 'utf-8', 'strict' ), Modified: python/branches/release26-maint/Misc/NEWS ============================================================================== --- python/branches/release26-maint/Misc/NEWS (original) +++ python/branches/release26-maint/Misc/NEWS Mon Aug 2 20:10:09 2010 @@ -35,9 +35,6 @@ when turned into an exception: in this case the exception simply gets ignored. -- Issue #7372: Fix pstats regression when stripping paths from profile - data generated with the profile module. - - Issue #4108: In urllib.robotparser, if there are multiple 'User-agent: *' entries, consider the first one. @@ -422,6 +419,10 @@ Tests ----- +- Issue #7849: Now the utility ``check_warnings`` verifies if the warnings are + effectively raised. A new private utility ``_check_py3k_warnings`` has been + backported to help silencing py3k warnings. + - Issue #8672: Add a zlib test ensuring that an incomplete stream can be handled by a decompressor object without errors (it returns incomplete uncompressed data). From python-checkins at python.org Mon Aug 2 20:10:13 2010 From: python-checkins at python.org (georg.brandl) Date: Mon, 2 Aug 2010 20:10:13 +0200 (CEST) Subject: [Python-checkins] r83538 - python/branches/py3k/Doc/reference/compound_stmts.rst Message-ID: <20100802181013.CD26AEEB21@mail.python.org> Author: georg.brandl Date: Mon Aug 2 20:10:13 2010 New Revision: 83538 Log: #6928: fix class docs w.r.t. new metaclasses. Modified: python/branches/py3k/Doc/reference/compound_stmts.rst Modified: python/branches/py3k/Doc/reference/compound_stmts.rst ============================================================================== --- python/branches/py3k/Doc/reference/compound_stmts.rst (original) +++ python/branches/py3k/Doc/reference/compound_stmts.rst Mon Aug 2 20:10:13 2010 @@ -552,24 +552,27 @@ A class definition defines a class object (see section :ref:`types`): -.. XXX need to document PEP 3115 changes here (new metaclasses) - .. productionlist:: classdef: [`decorators`] "class" `classname` [`inheritance`] ":" `suite` - inheritance: "(" [`expression_list`] ")" + inheritance: "(" [`argument_list` [","] ] ")" classname: `identifier` -A class definition is an executable statement. It first evaluates the -inheritance list, if present. Each item in the inheritance list should evaluate -to a class object or class type which allows subclassing. The class's suite is -then executed in a new execution frame (see section :ref:`naming`), using a -newly created local namespace and the original global namespace. (Usually, the -suite contains only function definitions.) When the class's suite finishes -execution, its execution frame is discarded but its local namespace is -saved. [#]_ A class object is then created using the inheritance list for the -base classes and the saved local namespace for the attribute dictionary. The -class name is bound to this class object in the original local namespace. +A class definition is an executable statement. The inheritance list usually +gives a list of base classes (see :ref:`metaclasses` for more advanced uses), so +each item in the list should evaluate to a class object which allows +subclassing. + +The class's suite is then executed in a new execution frame (see :ref:`naming`), +using a newly created local namespace and the original global namespace. +(Usually, the suite contains mostly function definitions.) When the class's +suite finishes execution, its execution frame is discarded but its local +namespace is saved. [#]_ A class object is then created using the inheritance +list for the base classes and the saved local namespace for the attribute +dictionary. The class name is bound to this class object in the original local +namespace. + +Class creation can be customized heavily using :ref:`metaclasses `. Classes can also be decorated; as with functions, :: @@ -583,25 +586,20 @@ Foo = f1(arg)(f2(Foo)) **Programmer's note:** Variables defined in the class definition are class -variables; they are shared by instances. Instance variables can be set in a -method with ``self.name = value``. Both class and instance variables are -accessible through the notation "``self.name``", and an instance variable hides -a class variable with the same name when accessed in this way. Class variables -can be used as defaults for instance variables, but using mutable values there -can lead to unexpected results. Descriptors can be used to create instance -variables with different implementation details. +attributes; they are shared by instances. Instance attributes can be set in a +method with ``self.name = value``. Both class and instance attributes are +accessible through the notation "``self.name``", and an instance attribute hides +a class attribute with the same name when accessed in this way. Class +attributes can be used as defaults for instance attributes, but using mutable +values there can lead to unexpected results. :ref:`Descriptors ` +can be used to create instance variables with different implementation details. -.. XXX add link to descriptor docs above .. seealso:: + :pep:`3116` - Metaclasses in Python 3 :pep:`3129` - Class Decorators -Class definitions, like function definitions, may be wrapped by one or more -:term:`decorator` expressions. The evaluation rules for the decorator -expressions are the same as for functions. The result must be a class object, -which is then bound to the class name. - .. rubric:: Footnotes From python-checkins at python.org Mon Aug 2 20:30:48 2010 From: python-checkins at python.org (georg.brandl) Date: Mon, 2 Aug 2010 20:30:48 +0200 (CEST) Subject: [Python-checkins] r83539 - python/branches/release26-maint/Doc/library/cgi.rst Message-ID: <20100802183048.8DFD8EE9B9@mail.python.org> Author: georg.brandl Date: Mon Aug 2 20:30:48 2010 New Revision: 83539 Log: #9061: warn that single quotes are not escaped. Modified: python/branches/release26-maint/Doc/library/cgi.rst Modified: python/branches/release26-maint/Doc/library/cgi.rst ============================================================================== --- python/branches/release26-maint/Doc/library/cgi.rst (original) +++ python/branches/release26-maint/Doc/library/cgi.rst Mon Aug 2 20:30:48 2010 @@ -349,10 +349,13 @@ Convert the characters ``'&'``, ``'<'`` and ``'>'`` in string *s* to HTML-safe sequences. Use this if you need to display text that might contain such characters in HTML. If the optional flag *quote* is true, the quotation mark - character (``'"'``) is also translated; this helps for inclusion in an HTML - attribute value, as in ````. If the value to be quoted might - include single- or double-quote characters, or both, consider using the - :func:`quoteattr` function in the :mod:`xml.sax.saxutils` module instead. + character (``"``) is also translated; this helps for inclusion in an HTML + attribute value delimited by double quotes, as in ````. Note + that single quotes are never translated. + + If the value to be quoted might include single- or double-quote characters, + or both, consider using the :func:`quoteattr` function in the + :mod:`xml.sax.saxutils` module instead. .. _cgi-security: From python-checkins at python.org Mon Aug 2 20:40:55 2010 From: python-checkins at python.org (ezio.melotti) Date: Mon, 2 Aug 2010 20:40:55 +0200 (CEST) Subject: [Python-checkins] r83540 - in python/branches/release26-maint: Lib/ctypes/test/test_pep3118.py Lib/ctypes/test/test_structures.py Lib/email/test/test_email.py Lib/email/test/test_email_renamed.py Lib/sqlite3/test/types.py Lib/sqlite3/test/userfunctions.py Lib/test/infinite_reload.py Lib/test/inspect_fodder.py Lib/test/regrtest.py Lib/test/test_binop.py Lib/test/test_compiler.py Lib/test/test_descrtut.py Lib/test/test_dict.py Lib/test/test_file.py Lib/test/test_ftplib.py Lib/test/test_functools.py Lib/test/test_grammar.py Lib/test/test_import.py Lib/test/test_importhooks.py Lib/test/test_inspect.py Lib/test/test_io.py Lib/test/test_itertools.py Lib/test/test_mutants.py Lib/test/test_opcodes.py Lib/test/test_optparse.py Lib/test/test_ossaudiodev.py Lib/test/test_pkgimport.py Lib/test/test_pyexpat.py Lib/test/test_queue.py Lib/test/test_random.py Lib/test/test_repr.py Lib/test/test_rfc822.py Lib/test/test_site.py Lib/test/test_sys.py Lib/test/test_threadsignals.py Lib/test/test_trace.py Lib/test/test_traceback.py Lib/test/test_with.py Lib/test/test_wsgiref.py Lib/test/test_xml_etree.py Lib/test/test_xml_etree_c.py Message-ID: <20100802184055.82463EE9BB@mail.python.org> Author: ezio.melotti Date: Mon Aug 2 20:40:55 2010 New Revision: 83540 Log: Merged revisions 77942,79023 via svnmerge from svn+ssh://pythondev at svn.python.org/python/trunk ........ r77942 | ezio.melotti | 2010-02-03 07:37:26 +0200 (Wed, 03 Feb 2010) | 1 line #7092: Silence more py3k warnings. Patch by Florent Xicluna. ........ r79023 | ezio.melotti | 2010-03-17 15:52:48 +0200 (Wed, 17 Mar 2010) | 1 line #7092: silence some more py3k warnings. ........ Modified: python/branches/release26-maint/ (props changed) python/branches/release26-maint/Lib/ctypes/test/test_pep3118.py python/branches/release26-maint/Lib/ctypes/test/test_structures.py python/branches/release26-maint/Lib/email/test/test_email.py python/branches/release26-maint/Lib/email/test/test_email_renamed.py python/branches/release26-maint/Lib/sqlite3/test/types.py python/branches/release26-maint/Lib/sqlite3/test/userfunctions.py python/branches/release26-maint/Lib/test/infinite_reload.py python/branches/release26-maint/Lib/test/inspect_fodder.py python/branches/release26-maint/Lib/test/regrtest.py python/branches/release26-maint/Lib/test/test_binop.py python/branches/release26-maint/Lib/test/test_compiler.py python/branches/release26-maint/Lib/test/test_descrtut.py python/branches/release26-maint/Lib/test/test_dict.py python/branches/release26-maint/Lib/test/test_file.py python/branches/release26-maint/Lib/test/test_ftplib.py python/branches/release26-maint/Lib/test/test_functools.py python/branches/release26-maint/Lib/test/test_grammar.py python/branches/release26-maint/Lib/test/test_import.py python/branches/release26-maint/Lib/test/test_importhooks.py python/branches/release26-maint/Lib/test/test_inspect.py python/branches/release26-maint/Lib/test/test_io.py python/branches/release26-maint/Lib/test/test_itertools.py python/branches/release26-maint/Lib/test/test_mutants.py python/branches/release26-maint/Lib/test/test_opcodes.py python/branches/release26-maint/Lib/test/test_optparse.py python/branches/release26-maint/Lib/test/test_ossaudiodev.py python/branches/release26-maint/Lib/test/test_pkgimport.py python/branches/release26-maint/Lib/test/test_pyexpat.py python/branches/release26-maint/Lib/test/test_queue.py python/branches/release26-maint/Lib/test/test_random.py python/branches/release26-maint/Lib/test/test_repr.py python/branches/release26-maint/Lib/test/test_rfc822.py python/branches/release26-maint/Lib/test/test_site.py python/branches/release26-maint/Lib/test/test_sys.py python/branches/release26-maint/Lib/test/test_threadsignals.py python/branches/release26-maint/Lib/test/test_trace.py python/branches/release26-maint/Lib/test/test_traceback.py python/branches/release26-maint/Lib/test/test_with.py python/branches/release26-maint/Lib/test/test_wsgiref.py python/branches/release26-maint/Lib/test/test_xml_etree.py python/branches/release26-maint/Lib/test/test_xml_etree_c.py Modified: python/branches/release26-maint/Lib/ctypes/test/test_pep3118.py ============================================================================== --- python/branches/release26-maint/Lib/ctypes/test/test_pep3118.py (original) +++ python/branches/release26-maint/Lib/ctypes/test/test_pep3118.py Mon Aug 2 20:40:55 2010 @@ -24,7 +24,7 @@ else: size = sizeof(ob) for dim in self.shape: - size /= dim + size //= dim self.itemsize = size self.strides = None self.readonly = False Modified: python/branches/release26-maint/Lib/ctypes/test/test_structures.py ============================================================================== --- python/branches/release26-maint/Lib/ctypes/test/test_structures.py (original) +++ python/branches/release26-maint/Lib/ctypes/test/test_structures.py Mon Aug 2 20:40:55 2010 @@ -367,11 +367,11 @@ _fields_ = [("d", c_int), ("e", c_int), ("f", c_int)] z = Z(1, 2, 3, 4, 5, 6) - self.failUnlessEqual((z.a, z.b, z.c, z.d, z.e, z.f), - (1, 2, 3, 4, 5, 6)) + self.assertEqual((z.a, z.b, z.c, z.d, z.e, z.f), + (1, 2, 3, 4, 5, 6)) z = Z(1) - self.failUnlessEqual((z.a, z.b, z.c, z.d, z.e, z.f), - (1, 0, 0, 0, 0, 0)) + self.assertEqual((z.a, z.b, z.c, z.d, z.e, z.f), + (1, 0, 0, 0, 0, 0)) self.assertRaises(TypeError, lambda: Z(1, 2, 3, 4, 5, 6, 7)) class PointerMemberTestCase(unittest.TestCase): Modified: python/branches/release26-maint/Lib/email/test/test_email.py ============================================================================== --- python/branches/release26-maint/Lib/email/test/test_email.py (original) +++ python/branches/release26-maint/Lib/email/test/test_email.py Mon Aug 2 20:40:55 2010 @@ -1086,7 +1086,7 @@ sign = '-' else: sign = '+' - tzoffset = ' %s%04d' % (sign, tzsecs / 36) + tzoffset = ' %s%04d' % (sign, tzsecs // 36) container['Date'] = time.strftime( '%a, %d %b %Y %H:%M:%S', time.localtime(now)) + tzoffset Modified: python/branches/release26-maint/Lib/email/test/test_email_renamed.py ============================================================================== --- python/branches/release26-maint/Lib/email/test/test_email_renamed.py (original) +++ python/branches/release26-maint/Lib/email/test/test_email_renamed.py Mon Aug 2 20:40:55 2010 @@ -1044,7 +1044,7 @@ sign = '-' else: sign = '+' - tzoffset = ' %s%04d' % (sign, tzsecs / 36) + tzoffset = ' %s%04d' % (sign, tzsecs // 36) container['Date'] = time.strftime( '%a, %d %b %Y %H:%M:%S', time.localtime(now)) + tzoffset Modified: python/branches/release26-maint/Lib/sqlite3/test/types.py ============================================================================== --- python/branches/release26-maint/Lib/sqlite3/test/types.py (original) +++ python/branches/release26-maint/Lib/sqlite3/test/types.py Mon Aug 2 20:40:55 2010 @@ -226,7 +226,7 @@ sqlite.converters["FOO"] = lambda x: "[%s]" % x sqlite.converters["BAR"] = lambda x: "<%s>" % x - sqlite.converters["EXC"] = lambda x: 5/0 + sqlite.converters["EXC"] = lambda x: 5 // 0 sqlite.converters["B1B1"] = lambda x: "MARKER" def tearDown(self): Modified: python/branches/release26-maint/Lib/sqlite3/test/userfunctions.py ============================================================================== --- python/branches/release26-maint/Lib/sqlite3/test/userfunctions.py (original) +++ python/branches/release26-maint/Lib/sqlite3/test/userfunctions.py Mon Aug 2 20:40:55 2010 @@ -38,7 +38,7 @@ def func_returnblob(): return buffer("blob") def func_raiseexception(): - 5/0 + 5 // 0 def func_isstring(v): return type(v) is unicode @@ -67,7 +67,7 @@ class AggrExceptionInInit: def __init__(self): - 5/0 + 5 // 0 def step(self, x): pass @@ -80,7 +80,7 @@ pass def step(self, x): - 5/0 + 5 // 0 def finalize(self): return 42 @@ -93,7 +93,7 @@ pass def finalize(self): - 5/0 + 5 // 0 class AggrCheckType: def __init__(self): Modified: python/branches/release26-maint/Lib/test/infinite_reload.py ============================================================================== --- python/branches/release26-maint/Lib/test/infinite_reload.py (original) +++ python/branches/release26-maint/Lib/test/infinite_reload.py Mon Aug 2 20:40:55 2010 @@ -3,5 +3,6 @@ # reload()ing. This module is imported by test_import.py:test_infinite_reload # to make sure this doesn't happen any more. +import imp import infinite_reload -reload(infinite_reload) +imp.reload(infinite_reload) Modified: python/branches/release26-maint/Lib/test/inspect_fodder.py ============================================================================== --- python/branches/release26-maint/Lib/test/inspect_fodder.py (original) +++ python/branches/release26-maint/Lib/test/inspect_fodder.py Mon Aug 2 20:40:55 2010 @@ -15,7 +15,7 @@ fr = inspect.currentframe() st = inspect.stack() p = x - q = y / 0 + q = y // 0 # line 20 class StupidGit: Modified: python/branches/release26-maint/Lib/test/regrtest.py ============================================================================== --- python/branches/release26-maint/Lib/test/regrtest.py (original) +++ python/branches/release26-maint/Lib/test/regrtest.py Mon Aug 2 20:40:55 2010 @@ -133,6 +133,7 @@ # keep a reference to the ascii module to workaround #7140 bug # (see issue #7027) import encodings.ascii +import imp # I see no other way to suppress these warnings; # putting them in test_grammar.py has no effect: @@ -657,7 +658,7 @@ indirect_test() else: def run_the_test(): - reload(the_module) + imp.reload(the_module) deltas = [] nwarmup, ntracked, fname = huntrleaks Modified: python/branches/release26-maint/Lib/test/test_binop.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_binop.py (original) +++ python/branches/release26-maint/Lib/test/test_binop.py Mon Aug 2 20:40:55 2010 @@ -207,6 +207,9 @@ """Compare two Rats for inequality.""" return not self == other + # Silence Py3k warning + __hash__ = None + class RatTestCase(unittest.TestCase): """Unit tests for Rat class and its support utilities.""" Modified: python/branches/release26-maint/Lib/test/test_compiler.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_compiler.py (original) +++ python/branches/release26-maint/Lib/test/test_compiler.py Mon Aug 2 20:40:55 2010 @@ -75,7 +75,7 @@ def testTryExceptFinally(self): # Test that except and finally clauses in one try stmt are recognized - c = compiler.compile("try:\n 1/0\nexcept:\n e = 1\nfinally:\n f = 1", + c = compiler.compile("try:\n 1//0\nexcept:\n e = 1\nfinally:\n f = 1", "", "exec") dct = {} exec c in dct Modified: python/branches/release26-maint/Lib/test/test_descrtut.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_descrtut.py (original) +++ python/branches/release26-maint/Lib/test/test_descrtut.py Mon Aug 2 20:40:55 2010 @@ -66,7 +66,7 @@ statement or the built-in function eval(): >>> def sorted(seq): - ... seq.sort() + ... seq.sort(key=str) ... return seq >>> print sorted(a.keys()) [1, 2] Modified: python/branches/release26-maint/Lib/test/test_dict.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_dict.py (original) +++ python/branches/release26-maint/Lib/test/test_dict.py Mon Aug 2 20:40:55 2010 @@ -582,11 +582,14 @@ type2test = Dict def test_main(): - test_support.run_unittest( - DictTest, - GeneralMappingTests, - SubclassMappingTests, - ) + with test_support._check_py3k_warnings( + ('dict(.has_key..| inequality comparisons) not supported in 3.x', + DeprecationWarning)): + test_support.run_unittest( + DictTest, + GeneralMappingTests, + SubclassMappingTests, + ) if __name__ == "__main__": test_main() Modified: python/branches/release26-maint/Lib/test/test_file.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_file.py (original) +++ python/branches/release26-maint/Lib/test/test_file.py Mon Aug 2 20:40:55 2010 @@ -118,7 +118,7 @@ self.assertEquals(self.f.__exit__(None, None, None), None) # it must also return None if an exception was given try: - 1/0 + 1 // 0 except: self.assertEquals(self.f.__exit__(*sys.exc_info()), None) Modified: python/branches/release26-maint/Lib/test/test_ftplib.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_ftplib.py (original) +++ python/branches/release26-maint/Lib/test/test_ftplib.py Mon Aug 2 20:40:55 2010 @@ -90,7 +90,8 @@ sock.listen(5) sock.settimeout(2) ip, port = sock.getsockname()[:2] - ip = ip.replace('.', ','); p1 = port / 256; p2 = port % 256 + ip = ip.replace('.', ',') + p1, p2 = divmod(port, 256) self.push('227 entering passive mode (%s,%d,%d)' %(ip, p1, p2)) conn, addr = sock.accept() self.dtp = DummyDTPHandler(conn, baseclass=self) Modified: python/branches/release26-maint/Lib/test/test_functools.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_functools.py (original) +++ python/branches/release26-maint/Lib/test/test_functools.py Mon Aug 2 20:40:55 2010 @@ -119,7 +119,7 @@ def test_error_propagation(self): def f(x, y): - x / y + x // y self.assertRaises(ZeroDivisionError, self.thetype(f, 1, 0)) self.assertRaises(ZeroDivisionError, self.thetype(f, 1), 0) self.assertRaises(ZeroDivisionError, self.thetype(f), 1, 0) Modified: python/branches/release26-maint/Lib/test/test_grammar.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_grammar.py (original) +++ python/branches/release26-maint/Lib/test/test_grammar.py Mon Aug 2 20:40:55 2010 @@ -8,7 +8,8 @@ # regression test, the filterwarnings() call has been added to # regrtest.py. -from test.test_support import run_unittest, check_syntax_error +from test.test_support import (run_unittest, check_syntax_error, + _check_py3k_warnings) import unittest import sys # testing import * @@ -152,8 +153,9 @@ f1(*(), **{}) def f2(one_argument): pass def f3(two, arguments): pass - def f4(two, (compound, (argument, list))): pass - def f5((compound, first), two): pass + # Silence Py3k warning + exec('def f4(two, (compound, (argument, list))): pass') + exec('def f5((compound, first), two): pass') self.assertEquals(f2.func_code.co_varnames, ('one_argument',)) self.assertEquals(f3.func_code.co_varnames, ('two', 'arguments')) if sys.platform.startswith('java'): @@ -172,7 +174,8 @@ def v0(*rest): pass def v1(a, *rest): pass def v2(a, b, *rest): pass - def v3(a, (b, c), *rest): return a, b, c, rest + # Silence Py3k warning + exec('def v3(a, (b, c), *rest): return a, b, c, rest') f1() f2(1) @@ -277,9 +280,10 @@ d22v(*(1, 2, 3, 4)) d22v(1, 2, *(3, 4, 5)) d22v(1, *(2, 3), **{'d': 4}) - def d31v((x)): pass + # Silence Py3k warning + exec('def d31v((x)): pass') + exec('def d32v((x,)): pass') d31v(1) - def d32v((x,)): pass d32v((1,)) # keyword arguments after *arglist @@ -474,7 +478,7 @@ continue except: raise - if count > 2 or big_hippo <> 1: + if count > 2 or big_hippo != 1: self.fail("continue then break in try/except in loop broken!") test_inner() @@ -536,7 +540,7 @@ if z != 2: self.fail('exec u\'z=1+1\'')""" g = {} exec 'z = 1' in g - if g.has_key('__builtins__'): del g['__builtins__'] + if '__builtins__' in g: del g['__builtins__'] if g != {'z': 1}: self.fail('exec \'z = 1\' in g') g = {} l = {} @@ -544,8 +548,8 @@ import warnings warnings.filterwarnings("ignore", "global statement", module="") exec 'global a; a = 1; b = 2' in g, l - if g.has_key('__builtins__'): del g['__builtins__'] - if l.has_key('__builtins__'): del l['__builtins__'] + if '__builtins__' in g: del g['__builtins__'] + if '__builtins__' in l: del l['__builtins__'] if (g, l) != ({'a':1}, {'b':2}): self.fail('exec ... in g (%s), l (%s)' %(g,l)) @@ -677,7 +681,6 @@ x = (1 == 1) if 1 == 1: pass if 1 != 1: pass - if 1 <> 1: pass if 1 < 1: pass if 1 > 1: pass if 1 <= 1: pass @@ -686,7 +689,10 @@ if 1 is not 1: pass if 1 in (): pass if 1 not in (): pass - if 1 < 1 > 1 == 1 >= 1 <= 1 <> 1 != 1 in 1 not in 1 is 1 is not 1: pass + if 1 < 1 > 1 == 1 >= 1 <= 1 != 1 in 1 not in 1 is 1 is not 1: pass + # Silence Py3k warning + if eval('1 <> 1'): pass + if eval('1 < 1 > 1 == 1 >= 1 <= 1 <> 1 != 1 in 1 not in 1 is 1 is not 1'): pass def testBinaryMaskOps(self): x = 1 & 1 @@ -769,9 +775,10 @@ x = {'one': 1, 'two': 2,} x = {'one': 1, 'two': 2, 'three': 3, 'four': 4, 'five': 5, 'six': 6} - x = `x` - x = `1 or 2 or 3` - self.assertEqual(`1,2`, '(1, 2)') + # Silence Py3k warning + x = eval('`x`') + x = eval('`1 or 2 or 3`') + self.assertEqual(eval('`1,2`'), '(1, 2)') x = x x = 'x' @@ -948,7 +955,13 @@ def test_main(): - run_unittest(TokenTests, GrammarTests) + with _check_py3k_warnings( + ("backquote not supported", SyntaxWarning), + ("tuple parameter unpacking has been removed", SyntaxWarning), + ("parenthesized argument names are invalid", SyntaxWarning), + ("classic int division", DeprecationWarning), + (".+ not supported in 3.x", DeprecationWarning)): + run_unittest(TokenTests, GrammarTests) if __name__ == '__main__': test_main() Modified: python/branches/release26-maint/Lib/test/test_import.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_import.py (original) +++ python/branches/release26-maint/Lib/test/test_import.py Mon Aug 2 20:40:55 2010 @@ -7,6 +7,7 @@ import py_compile import warnings import marshal +import imp from test.test_support import (unlink, TESTFN, unload, run_unittest, check_warnings, TestFailed) @@ -56,11 +57,10 @@ f.close() try: - try: - mod = __import__(TESTFN) - except ImportError, err: - self.fail("import from %s failed: %s" % (ext, err)) - + mod = __import__(TESTFN) + except ImportError, err: + self.fail("import from %s failed: %s" % (ext, err)) + else: self.assertEquals(mod.a, a, "module loaded (%s) but contents invalid" % mod) self.assertEquals(mod.b, b, @@ -69,10 +69,9 @@ os.unlink(source) try: - try: - reload(mod) - except ImportError, err: - self.fail("import from .pyc/.pyo failed: %s" % err) + imp.reload(mod) + except ImportError, err: + self.fail("import from .pyc/.pyo failed: %s" % err) finally: try: os.unlink(pyc) @@ -159,7 +158,7 @@ def test_failing_import_sticks(self): source = TESTFN + os.extsep + "py" f = open(source, "w") - print >> f, "a = 1/0" + print >> f, "a = 1 // 0" f.close() # New in 2.4, we shouldn't be able to import that no matter how often @@ -205,7 +204,7 @@ print >> f, "b = 20//0" f.close() - self.assertRaises(ZeroDivisionError, reload, mod) + self.assertRaises(ZeroDivisionError, imp.reload, mod) # But we still expect the module to be in sys.modules. mod = sys.modules.get(TESTFN) Modified: python/branches/release26-maint/Lib/test/test_importhooks.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_importhooks.py (original) +++ python/branches/release26-maint/Lib/test/test_importhooks.py Mon Aug 2 20:40:55 2010 @@ -180,7 +180,7 @@ self.failIf(hasattr(reloadmodule,'reloaded')) TestImporter.modules['reloadmodule'] = (False, reload_co) - reload(reloadmodule) + imp.reload(reloadmodule) self.failUnless(hasattr(reloadmodule,'reloaded')) import hooktestpackage.oldabs @@ -247,9 +247,10 @@ for n in sys.modules.keys(): if n.startswith(parent): del sys.modules[n] - for mname in mnames: - m = __import__(mname, globals(), locals(), ["__dummy__"]) - m.__loader__ # to make sure we actually handled the import + with test_support._check_py3k_warnings(): + for mname in mnames: + m = __import__(mname, globals(), locals(), ["__dummy__"]) + m.__loader__ # to make sure we actually handled the import def test_main(): Modified: python/branches/release26-maint/Lib/test/test_inspect.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_inspect.py (original) +++ python/branches/release26-maint/Lib/test/test_inspect.py Mon Aug 2 20:40:55 2010 @@ -119,7 +119,7 @@ self.assertEqual(git.tr[1][1:], (modfile, 9, 'spam', [' eggs(b + d, c + f)\n'], 0)) self.assertEqual(git.tr[2][1:], (modfile, 18, 'eggs', - [' q = y / 0\n'], 0)) + [' q = y // 0\n'], 0)) def test_frame(self): args, varargs, varkw, locals = inspect.getargvalues(mod.fr) Modified: python/branches/release26-maint/Lib/test/test_io.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_io.py (original) +++ python/branches/release26-maint/Lib/test/test_io.py Mon Aug 2 20:40:55 2010 @@ -248,11 +248,11 @@ f = None try: with open(test_support.TESTFN, "wb", bufsize) as f: - 1/0 + 1 // 0 except ZeroDivisionError: self.assertEqual(f.closed, True) else: - self.fail("1/0 didn't raise an exception") + self.fail("1 // 0 didn't raise an exception") # issue 5008 def test_append_mode_tell(self): Modified: python/branches/release26-maint/Lib/test/test_itertools.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_itertools.py (original) +++ python/branches/release26-maint/Lib/test/test_itertools.py Mon Aug 2 20:40:55 2010 @@ -7,6 +7,7 @@ import random import copy import pickle +from functools import reduce maxsize = test_support.MAX_Py_ssize_t minsize = -maxsize-1 @@ -112,7 +113,7 @@ values = [5*x-12 for x in range(n)] for r in range(n+2): result = list(combinations(values, r)) - self.assertEqual(len(result), 0 if r>n else fact(n) / fact(r) / fact(n-r)) # right number of combs + self.assertEqual(len(result), 0 if r>n else fact(n) // fact(r) // fact(n-r)) # right number of combs self.assertEqual(len(result), len(set(result))) # no repeats self.assertEqual(result, sorted(result)) # lexicographic order for c in result: @@ -176,7 +177,7 @@ values = [5*x-12 for x in range(n)] for r in range(n+2): result = list(permutations(values, r)) - self.assertEqual(len(result), 0 if r>n else fact(n) / fact(n-r)) # right number of perms + self.assertEqual(len(result), 0 if r>n else fact(n) // fact(n-r)) # right number of perms self.assertEqual(len(result), len(set(result))) # no repeats self.assertEqual(result, sorted(result)) # lexicographic order for p in result: @@ -370,7 +371,10 @@ [range(1000), range(0), range(3000,3050), range(1200), range(1500)], [range(1000), range(0), range(3000,3050), range(1200), range(1500), range(0)], ]: - target = map(None, *args) + # target = map(None, *args) <- this raises a py3k warning + # this is the replacement: + target = [tuple([arg[i] if i < len(arg) else None for arg in args]) + for i in range(max(map(len, args)))] self.assertEqual(list(izip_longest(*args)), target) self.assertEqual(list(izip_longest(*args, **{})), target) target = [tuple((e is None and 'X' or e) for e in t) for t in target] # Replace None fills with 'X' @@ -382,7 +386,8 @@ self.assertEqual(list(izip_longest([])), zip([])) self.assertEqual(list(izip_longest('abcdef')), zip('abcdef')) - self.assertEqual(list(izip_longest('abc', 'defg', **{})), map(None, 'abc', 'defg')) # empty keyword dict + self.assertEqual(list(izip_longest('abc', 'defg', **{})), + zip(list('abc') + [None], 'defg')) # empty keyword dict self.assertRaises(TypeError, izip_longest, 3) self.assertRaises(TypeError, izip_longest, range(3), 3) @@ -1228,7 +1233,7 @@ # is differencing with a range so that consecutive numbers all appear in # same group. >>> data = [ 1, 4,5,6, 10, 15,16,17,18, 22, 25,26,27,28] ->>> for k, g in groupby(enumerate(data), lambda (i,x):i-x): +>>> for k, g in groupby(enumerate(data), lambda t:t[0]-t[1]): ... print map(operator.itemgetter(1), g) ... [1] Modified: python/branches/release26-maint/Lib/test/test_mutants.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_mutants.py (original) +++ python/branches/release26-maint/Lib/test/test_mutants.py Mon Aug 2 20:40:55 2010 @@ -210,7 +210,7 @@ # Tim sez: "luck of the draw; crashes with or without for me." print >> f - return `"machiavelli"` + return repr("machiavelli") def __hash__(self): return 0 Modified: python/branches/release26-maint/Lib/test/test_opcodes.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_opcodes.py (original) +++ python/branches/release26-maint/Lib/test/test_opcodes.py Mon Aug 2 20:40:55 2010 @@ -1,6 +1,6 @@ # Python test set -- part 2, opcodes -from test.test_support import run_unittest +from test.test_support import run_unittest, _check_py3k_warnings import unittest class OpcodeTest(unittest.TestCase): @@ -9,7 +9,7 @@ n = 0 for i in range(10): n = n+i - try: 1/0 + try: 1 // 0 except NameError: pass except ZeroDivisionError: pass except TypeError: pass @@ -104,7 +104,12 @@ def test_main(): - run_unittest(OpcodeTest) + with _check_py3k_warnings(("exceptions must derive from BaseException", + DeprecationWarning), + ("catching classes that don't inherit " + "from BaseException is not allowed", + DeprecationWarning)): + run_unittest(OpcodeTest) if __name__ == '__main__': test_main() Modified: python/branches/release26-maint/Lib/test/test_optparse.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_optparse.py (original) +++ python/branches/release26-maint/Lib/test/test_optparse.py Mon Aug 2 20:40:55 2010 @@ -26,12 +26,6 @@ from optparse import _match_abbrev from optparse import _parse_num -# Do the right thing with boolean values for all known Python versions. -try: - True, False -except NameError: - (True, False) = (1, 0) - retype = type(re.compile('')) class InterceptedError(Exception): Modified: python/branches/release26-maint/Lib/test/test_ossaudiodev.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_ossaudiodev.py (original) +++ python/branches/release26-maint/Lib/test/test_ossaudiodev.py Mon Aug 2 20:40:55 2010 @@ -44,7 +44,8 @@ try: dsp = ossaudiodev.open('w') except IOError, msg: - if msg[0] in (errno.EACCES, errno.ENOENT, errno.ENODEV, errno.EBUSY): + if msg.args[0] in (errno.EACCES, errno.ENOENT, + errno.ENODEV, errno.EBUSY): raise TestSkipped(msg) raise @@ -70,7 +71,7 @@ self.fail("dsp.%s not read-only" % attr) # Compute expected running time of sound sample (in seconds). - expected_time = float(len(data)) / (ssize/8) / nchannels / rate + expected_time = float(len(data)) / (ssize//8) / nchannels / rate # set parameters based on .au file headers dsp.setparameters(AFMT_S16_NE, nchannels, rate) @@ -161,7 +162,8 @@ try: dsp = ossaudiodev.open('w') except (ossaudiodev.error, IOError), msg: - if msg[0] in (errno.EACCES, errno.ENOENT, errno.ENODEV, errno.EBUSY): + if msg.args[0] in (errno.EACCES, errno.ENOENT, + errno.ENODEV, errno.EBUSY): raise TestSkipped(msg) raise dsp.close() Modified: python/branches/release26-maint/Lib/test/test_pkgimport.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_pkgimport.py (original) +++ python/branches/release26-maint/Lib/test/test_pkgimport.py Mon Aug 2 20:40:55 2010 @@ -6,14 +6,14 @@ def __init__(self, *args, **kw): self.package_name = 'PACKAGE_' - while sys.modules.has_key(self.package_name): + while self.package_name in sys.modules: self.package_name += random.choose(string.letters) self.module_name = self.package_name + '.foo' unittest.TestCase.__init__(self, *args, **kw) def remove_modules(self): for module_name in (self.package_name, self.module_name): - if sys.modules.has_key(module_name): + if module_name in sys.modules: del sys.modules[module_name] def setUp(self): @@ -52,8 +52,8 @@ try: __import__(self.module_name) except SyntaxError: pass else: raise RuntimeError, 'Failed to induce SyntaxError' - self.assert_(not sys.modules.has_key(self.module_name) and - not hasattr(sys.modules[self.package_name], 'foo')) + self.assertTrue(self.module_name not in sys.modules) + self.assertFalse(hasattr(sys.modules[self.package_name], 'foo')) # ...make up a variable name that isn't bound in __builtins__ var = 'a' Modified: python/branches/release26-maint/Lib/test/test_pyexpat.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_pyexpat.py (original) +++ python/branches/release26-maint/Lib/test/test_pyexpat.py Mon Aug 2 20:40:55 2010 @@ -554,7 +554,7 @@ self.n=0 parser.Parse(xml1, 0) - parser.buffer_size /= 2 + parser.buffer_size //= 2 self.assertEquals(parser.buffer_size, 1024) parser.Parse(xml2, 1) self.assertEquals(self.n, 4) Modified: python/branches/release26-maint/Lib/test/test_queue.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_queue.py (original) +++ python/branches/release26-maint/Lib/test/test_queue.py Mon Aug 2 20:40:55 2010 @@ -102,21 +102,23 @@ q.put(i) self.assert_(not q.empty(), "Queue should not be empty") self.assert_(not q.full(), "Queue should not be full") - q.put("last") + last = 2 * QUEUE_SIZE + full = 3 * 2 * QUEUE_SIZE + q.put(last) self.assert_(q.full(), "Queue should be full") try: - q.put("full", block=0) + q.put(full, block=0) self.fail("Didn't appear to block with a full queue") except Queue.Full: pass try: - q.put("full", timeout=0.01) + q.put(full, timeout=0.01) self.fail("Didn't appear to time-out with a full queue") except Queue.Full: pass # Test a blocking put - self.do_blocking_test(q.put, ("full",), q.get, ()) - self.do_blocking_test(q.put, ("full", True, 10), q.get, ()) + self.do_blocking_test(q.put, (full,), q.get, ()) + self.do_blocking_test(q.put, (full, True, 10), q.get, ()) # Empty it for i in range(QUEUE_SIZE): q.get() Modified: python/branches/release26-maint/Lib/test/test_random.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_random.py (original) +++ python/branches/release26-maint/Lib/test/test_random.py Mon Aug 2 20:40:55 2010 @@ -6,6 +6,7 @@ import pickle import warnings from math import log, exp, sqrt, pi, fsum as msum +from functools import reduce from test import test_support class TestBasicOps(unittest.TestCase): Modified: python/branches/release26-maint/Lib/test/test_repr.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_repr.py (original) +++ python/branches/release26-maint/Lib/test/test_repr.py Mon Aug 2 20:40:55 2010 @@ -8,7 +8,7 @@ import shutil import unittest -from test.test_support import run_unittest +from test.test_support import run_unittest, _check_py3k_warnings from repr import repr as r # Don't shadow builtin repr from repr import Repr @@ -174,7 +174,8 @@ def test_buffer(self): # XXX doesn't test buffers with no b_base or read-write buffers (see # bufferobject.c). The test is fairly incomplete too. Sigh. - x = buffer('foo') + with _check_py3k_warnings(): + x = buffer('foo') self.failUnless(repr(x).startswith(' Author: ezio.melotti Date: Mon Aug 2 20:43:03 2010 New Revision: 83541 Log: Unblocked revisions 79165 via svnmerge ........ r79165 | florent.xicluna | 2010-03-21 03:14:24 +0200 (Sun, 21 Mar 2010) | 2 lines #7092 - Silence more py3k deprecation warnings, using test_support.check_py3k_warnings() helper. ........ Modified: python/branches/release26-maint/ (props changed) From python-checkins at python.org Mon Aug 2 20:56:54 2010 From: python-checkins at python.org (georg.brandl) Date: Mon, 2 Aug 2010 20:56:54 +0200 (CEST) Subject: [Python-checkins] r83542 - in python/branches/py3k/Lib/test: test_SimpleHTTPServer.py test_httpservers.py Message-ID: <20100802185654.4CDDDEE9D8@mail.python.org> Author: georg.brandl Date: Mon Aug 2 20:56:54 2010 New Revision: 83542 Log: Move test_SimpleHTTPServer into test_httpservers. Removed: python/branches/py3k/Lib/test/test_SimpleHTTPServer.py Modified: python/branches/py3k/Lib/test/test_httpservers.py Deleted: python/branches/py3k/Lib/test/test_SimpleHTTPServer.py ============================================================================== --- python/branches/py3k/Lib/test/test_SimpleHTTPServer.py Mon Aug 2 20:56:54 2010 +++ (empty file) @@ -1,41 +0,0 @@ -""" -These tests only check url parsing for now. -We don't want to require the 'network' resource. -""" - -import os, unittest -from http.server import SimpleHTTPRequestHandler -from test import support - - -class SocketlessRequestHandler(SimpleHTTPRequestHandler): - def __init__(self): - pass - -class SimpleHTTPRequestHandlerTestCase(unittest.TestCase): - """ Test url parsing """ - def setUp (self): - self.translated = os.getcwd() - self.translated = os.path.join(self.translated, 'filename') - self.handler = SocketlessRequestHandler () - - def test_queryArguments (self): - path = self.handler.translate_path ('/filename') - self.assertEquals (path, self.translated) - path = self.handler.translate_path ('/filename?foo=bar') - self.assertEquals (path, self.translated) - path = self.handler.translate_path ('/filename?a=b&spam=eggs#zot') - self.assertEquals (path, self.translated) - - def test_startWithDoubleSlash (self): - path = self.handler.translate_path ('//filename') - self.assertEquals (path, self.translated) - path = self.handler.translate_path ('//filename?foo=bar') - self.assertEquals (path, self.translated) - - -def test_main(): - support.run_unittest(SimpleHTTPRequestHandlerTestCase) - -if __name__ == "__main__": - test_main() Modified: python/branches/py3k/Lib/test/test_httpservers.py ============================================================================== --- python/branches/py3k/Lib/test/test_httpservers.py (original) +++ python/branches/py3k/Lib/test/test_httpservers.py Mon Aug 2 20:56:54 2010 @@ -401,13 +401,41 @@ (res.read(), res.getheader('Content-type'), res.status)) +class SocketlessRequestHandler(SimpleHTTPRequestHandler): + def __init__(self): + pass + +class SimpleHTTPRequestHandlerTestCase(unittest.TestCase): + """ Test url parsing """ + def setUp(self): + self.translated = os.getcwd() + self.translated = os.path.join(self.translated, 'filename') + self.handler = SocketlessRequestHandler() + + def test_query_arguments(self): + path = self.handler.translate_path('/filename') + self.assertEqual(path, self.translated) + path = self.handler.translate_path('/filename?foo=bar') + self.assertEqual(path, self.translated) + path = self.handler.translate_path('/filename?a=b&spam=eggs#zot') + self.assertEqual(path, self.translated) + + def test_start_with_double_slash(self): + path = self.handler.translate_path('//filename') + self.assertEqual(path, self.translated) + path = self.handler.translate_path('//filename?foo=bar') + self.assertEqual(path, self.translated) + + def test_main(verbose=None): + cwd = os.getcwd() try: - cwd = os.getcwd() - support.run_unittest(BaseHTTPServerTestCase, - SimpleHTTPServerTestCase, - CGIHTTPServerTestCase - ) + support.run_unittest( + BaseHTTPServerTestCase, + SimpleHTTPServerTestCase, + CGIHTTPServerTestCase, + SimpleHTTPRequestHandlerTestCase, + ) finally: os.chdir(cwd) From python-checkins at python.org Mon Aug 2 20:59:52 2010 From: python-checkins at python.org (georg.brandl) Date: Mon, 2 Aug 2010 20:59:52 +0200 (CEST) Subject: [Python-checkins] r83543 - python/branches/py3k/Lib/test/regrtest.py Message-ID: <20100802185952.CF6BDEEA92@mail.python.org> Author: georg.brandl Date: Mon Aug 2 20:59:52 2010 New Revision: 83543 Log: #8560: add progress indicator to regrtest. Modified: python/branches/py3k/Lib/test/regrtest.py Modified: python/branches/py3k/Lib/test/regrtest.py ============================================================================== --- python/branches/py3k/Lib/test/regrtest.py (original) +++ python/branches/py3k/Lib/test/regrtest.py Mon Aug 2 20:59:52 2010 @@ -390,7 +390,7 @@ sys.exit(0) else: print(("No handler for option {}. Please report this as a bug " - "at http://bugs.python.org.").format(o), file=sys.stderr) + "at http://bugs.python.org.").format(o), file=sys.stderr) sys.exit(1) if single and fromfile: usage("-s and -f don't go together!") @@ -517,6 +517,9 @@ else: tests = iter(selected) + tests = list(tests) + test_count = len(tests) + test_count_width = len(str(test_count)) if use_mp: try: from threading import Thread @@ -559,8 +562,6 @@ output.put((None, None, None, None)) return result = json.loads(result) - if not quiet: - stdout = test+'\n'+stdout output.put((test, stdout.rstrip(), stderr.rstrip(), result)) except BaseException: output.put((None, None, None, None)) @@ -569,12 +570,16 @@ for worker in workers: worker.start() finished = 0 + test_index = 1 try: while finished < use_mp: test, stdout, stderr, result = output.get() if test is None: finished += 1 continue + if not quiet: + print("[{1:{0}}/{2:{0}}] {3}".format( + test_count_width, test_index, test_count, test)) if stdout: print(stdout) if stderr: @@ -583,15 +588,17 @@ assert result[1] == 'KeyboardInterrupt' raise KeyboardInterrupt # What else? accumulate_result(test, result) + test_index += 1 except KeyboardInterrupt: interrupted = True pending.close() for worker in workers: worker.join() else: - for test in tests: + for test_index, test in enumerate(tests, 1): if not quiet: - print(test) + print("[{1:{0}}/{2:{0}}] {3}".format( + test_count_width, test_index, test_count, test)) sys.stdout.flush() if trace: # If we're tracing code coverage, then we don't exit with status From python-checkins at python.org Mon Aug 2 21:00:34 2010 From: python-checkins at python.org (georg.brandl) Date: Mon, 2 Aug 2010 21:00:34 +0200 (CEST) Subject: [Python-checkins] r83544 - python/branches/release31-maint Message-ID: <20100802190034.B561EEEA15@mail.python.org> Author: georg.brandl Date: Mon Aug 2 21:00:34 2010 New Revision: 83544 Log: Blocked revisions 83543 via svnmerge ........ r83543 | georg.brandl | 2010-08-02 20:59:52 +0200 (Mo, 02 Aug 2010) | 1 line #8560: add progress indicator to regrtest. ........ Modified: python/branches/release31-maint/ (props changed) From python-checkins at python.org Mon Aug 2 21:01:34 2010 From: python-checkins at python.org (georg.brandl) Date: Mon, 2 Aug 2010 21:01:34 +0200 (CEST) Subject: [Python-checkins] r83545 - python/branches/release27-maint Message-ID: <20100802190134.1CCB7EEA70@mail.python.org> Author: georg.brandl Date: Mon Aug 2 21:01:33 2010 New Revision: 83545 Log: Blocked revisions 83543 via svnmerge ........ r83543 | georg.brandl | 2010-08-02 20:59:52 +0200 (Mo, 02 Aug 2010) | 1 line #8560: add progress indicator to regrtest. ........ Modified: python/branches/release27-maint/ (props changed) From python-checkins at python.org Mon Aug 2 21:16:34 2010 From: python-checkins at python.org (georg.brandl) Date: Mon, 2 Aug 2010 21:16:34 +0200 (CEST) Subject: [Python-checkins] r83546 - in python/branches/py3k: Doc/distutils/builtdist.rst Lib/distutils/command/bdist_msi.py Lib/distutils/command/bdist_wininst.py Message-ID: <20100802191634.C7F0CEEA0A@mail.python.org> Author: georg.brandl Date: Mon Aug 2 21:16:34 2010 New Revision: 83546 Log: #7973: Fix distutils options spelling. Modified: python/branches/py3k/Doc/distutils/builtdist.rst python/branches/py3k/Lib/distutils/command/bdist_msi.py python/branches/py3k/Lib/distutils/command/bdist_wininst.py Modified: python/branches/py3k/Doc/distutils/builtdist.rst ============================================================================== --- python/branches/py3k/Doc/distutils/builtdist.rst (original) +++ python/branches/py3k/Doc/distutils/builtdist.rst Mon Aug 2 21:16:34 2010 @@ -176,7 +176,7 @@ explicitly specify multiple :command:`bdist_\*` commands and their options:: python setup.py bdist_rpm --packager="John Doe " \ - bdist_wininst --target_version="2.0" + bdist_wininst --target-version="2.0" Creating RPM packages is driven by a :file:`.spec` file, much as using the Distutils is driven by the setup script. To make your life easier, the Modified: python/branches/py3k/Lib/distutils/command/bdist_msi.py ============================================================================== --- python/branches/py3k/Lib/distutils/command/bdist_msi.py (original) +++ python/branches/py3k/Lib/distutils/command/bdist_msi.py Mon Aug 2 21:16:34 2010 @@ -148,7 +148,7 @@ if not self.skip_build and self.distribution.has_ext_modules()\ and self.target_version != short_version: raise DistutilsOptionError( - "target version can only be %s, or the '--skip_build'" + "target version can only be %s, or the '--skip-build'" " option must be specified" % (short_version,)) else: self.versions = list(self.all_versions) Modified: python/branches/py3k/Lib/distutils/command/bdist_wininst.py ============================================================================== --- python/branches/py3k/Lib/distutils/command/bdist_wininst.py (original) +++ python/branches/py3k/Lib/distutils/command/bdist_wininst.py Mon Aug 2 21:16:34 2010 @@ -89,7 +89,7 @@ short_version = get_python_version() if self.target_version and self.target_version != short_version: raise DistutilsOptionError( - "target version can only be %s, or the '--skip_build'" \ + "target version can only be %s, or the '--skip-build'" \ " option must be specified" % (short_version,)) self.target_version = short_version From python-checkins at python.org Mon Aug 2 21:19:26 2010 From: python-checkins at python.org (georg.brandl) Date: Mon, 2 Aug 2010 21:19:26 +0200 (CEST) Subject: [Python-checkins] r83547 - python/branches/py3k/Doc/library/os.path.rst Message-ID: <20100802191926.6AA8BEEA0A@mail.python.org> Author: georg.brandl Date: Mon Aug 2 21:19:26 2010 New Revision: 83547 Log: #7386: add example that shows that trailing path separators are stripped. Modified: python/branches/py3k/Doc/library/os.path.rst Modified: python/branches/py3k/Doc/library/os.path.rst ============================================================================== --- python/branches/py3k/Doc/library/os.path.rst (original) +++ python/branches/py3k/Doc/library/os.path.rst Mon Aug 2 21:19:26 2010 @@ -207,7 +207,9 @@ .. function:: normpath(path) Normalize a pathname. This collapses redundant separators and up-level - references so that ``A//B``, ``A/./B`` and ``A/foo/../B`` all become ``A/B``. + references so that ``A//B``, ``A/B/``, ``A/./B`` and ``A/foo/../B`` all become + ``A/B``. + It does not normalize the case (use :func:`normcase` for that). On Windows, it converts forward slashes to backward slashes. It should be understood that this may change the meaning of the path if it contains symbolic links! From python-checkins at python.org Mon Aug 2 21:23:34 2010 From: python-checkins at python.org (georg.brandl) Date: Mon, 2 Aug 2010 21:23:34 +0200 (CEST) Subject: [Python-checkins] r83548 - python/branches/py3k/Doc/library/functions.rst Message-ID: <20100802192334.BDD00EE9B9@mail.python.org> Author: georg.brandl Date: Mon Aug 2 21:23:34 2010 New Revision: 83548 Log: #8172: how does one use a property? Modified: python/branches/py3k/Doc/library/functions.rst Modified: python/branches/py3k/Doc/library/functions.rst ============================================================================== --- python/branches/py3k/Doc/library/functions.rst (original) +++ python/branches/py3k/Doc/library/functions.rst Mon Aug 2 21:23:34 2010 @@ -847,7 +847,7 @@ *fget* is a function for getting an attribute value, likewise *fset* is a function for setting, and *fdel* a function for del'ing, an attribute. Typical - use is to define a managed attribute x:: + use is to define a managed attribute ``x``:: class C(object): def __init__(self): @@ -861,6 +861,9 @@ del self._x x = property(getx, setx, delx, "I'm the 'x' property.") + If then *c* is an instance of *C*, ``c.x`` will invoke the getter, + ``c.x = value`` will invoke the setter and ``del c.x`` the deleter. + If given, *doc* will be the docstring of the property attribute. Otherwise, the property will copy *fget*'s docstring (if it exists). This makes it possible to create read-only properties easily using :func:`property` as a :term:`decorator`:: From python-checkins at python.org Mon Aug 2 21:29:03 2010 From: python-checkins at python.org (alexander.belopolsky) Date: Mon, 2 Aug 2010 21:29:03 +0200 (CEST) Subject: [Python-checkins] r83549 - python/branches/release26-maint/Doc/library/trace.rst Message-ID: <20100802192903.E9A49EEA8C@mail.python.org> Author: alexander.belopolsky Date: Mon Aug 2 21:29:03 2010 New Revision: 83549 Log: Issue 9264: Document trace module command line options. Patch by Eli Bendersky. Modified: python/branches/release26-maint/Doc/library/trace.rst Modified: python/branches/release26-maint/Doc/library/trace.rst ============================================================================== --- python/branches/release26-maint/Doc/library/trace.rst (original) +++ python/branches/release26-maint/Doc/library/trace.rst Mon Aug 2 21:29:03 2010 @@ -1,4 +1,3 @@ - :mod:`trace` --- Trace or track Python statement execution ========================================================== @@ -20,59 +19,100 @@ The :mod:`trace` module can be invoked from the command line. It can be as simple as :: - python -m trace --count somefile.py ... + python -m trace --count -C . somefile.py ... + +The above will execute :file:`somefile.py` and generate annotated listings of all +Python modules imported during the execution into the current directory. + +Meta-options +^^^^^^^^^^^^ + +``--help`` + + Display usage and exit. + +``--version`` + + Display the version of the module and exit. + +Main options +^^^^^^^^^^^^ + +The ``--listfuncs`` option is mutually exclusive with the ``--trace`` and +``--count`` options . When ``--listfuncs`` is provided, neither ``--counts`` +nor ``--trace`` are accepted, and vice versa. + +``--count, -c`` -The above will generate annotated listings of all Python modules imported during -the execution of :file:`somefile.py`. + Produce a set of annotated listing files upon program completion that shows + how many times each statement was executed. + See also ``--coverdir``, ``--file``, ``--no-report`` below. -The following command-line arguments are supported: +``--trace, -t`` -:option:`--trace`, :option:`-t` Display lines as they are executed. -:option:`--count`, :option:`-c` - Produce a set of annotated listing files upon program completion that shows how - many times each statement was executed. - -:option:`--report`, :option:`-r` - Produce an annotated list from an earlier program run that used the - :option:`--count` and :option:`--file` arguments. +``--listfuncs, -l`` -:option:`--no-report`, :option:`-R` - Do not generate annotated listings. This is useful if you intend to make - several runs with :option:`--count` then produce a single set of annotated - listings at the end. + Display the functions executed by running the program. + +``--report, -r`` + + Produce an annotated list from an earlier program run that used the ``--count`` + and ``--file`` option. Do not execute any code. + +``--trackcalls, -T`` -:option:`--listfuncs`, :option:`-l` - List the functions executed by running the program. + Display the calling relationships exposed by running the program. -:option:`--trackcalls`, :option:`-T` - Generate calling relationships exposed by running the program. +Modifiers +^^^^^^^^^ -:option:`--file`, :option:`-f` - Name a file containing (or to contain) counts. +``--file=, -f`` -:option:`--coverdir`, :option:`-C` - Name a directory in which to save annotated listing files. + Name of a file to accumulate counts over several tracing runs. Should be used + with the ``--count`` option. + +``--coverdir=, -C`` + + Directory where the report files go. The coverage report for + ``package.module`` is written to file ``dir/package/module.cover``. + +``--missing, -m`` -:option:`--missing`, :option:`-m` When generating annotated listings, mark lines which were not executed with '``>>>>>>``'. -:option:`--summary`, :option:`-s` - When using :option:`--count` or :option:`--report`, write a brief summary to +``--summary, -s`` + + When using ``--count`` or ``--report``, write a brief summary to stdout for each file processed. -:option:`--ignore-module` +``--no-report, -R`` + + Do not generate annotated listings. This is useful if you intend to make + several runs with ``--count`` then produce a single set of annotated + listings at the end. + +``--timing, -g`` + + Prefix each line with the time since the program started. Only used while + tracing. + +Filters +^^^^^^^ + +These options may be repeated multiple times. + +``--ignore-module=`` + Accepts comma separated list of module names. Ignore each of the named - module and its submodules (if it is a package). May be given - multiple times. + modules and its submodules (if it is a package). -:option:`--ignore-dir` - Ignore all modules and packages in the named directory and subdirectories - (multiple directories can be joined by os.pathsep). May be given multiple - times. +``--ignore-dir=`` + Ignore all modules and packages in the named directory and subdirectories + (multiple directories can be joined by ``os.pathsep``). .. _trace-api: @@ -80,7 +120,7 @@ --------------------- -.. class:: Trace([count=1[, trace=1[, countfuncs=0[, countcallers=0[, ignoremods=()[, ignoredirs=()[, infile=None[, outfile=None[, timing=False]]]]]]]]]) +.. class:: Trace(count=1, trace=1, countfuncs=0, countcallers=0, ignoremods=(), ignoredirs=(), infile=None, outfile=None, timing=False) Create an object to trace execution of a single statement or expression. All parameters are optional. *count* enables counting of line numbers. *trace* @@ -88,19 +128,20 @@ called during the run. *countcallers* enables call relationship tracking. *ignoremods* is a list of modules or packages to ignore. *ignoredirs* is a list of directories whose modules or packages should be ignored. *infile* is the - file from which to read stored count information. *outfile* is a file in which - to write updated count information. *timing* enables a timestamp relative - to when tracing was started to be displayed. + name of the file from which to read stored count information. *outfile* is + the name of the file in which to write updated count information. *timing* + enables a timestamp relative to when tracing was started to be displayed. .. method:: Trace.run(cmd) - Run *cmd* under control of the Trace object with the current tracing parameters. + Run *cmd* under control of the :class:`Trace` object with the current tracing parameters. + *cmd* must be a string or code object, suitable for passing into :func:`exec`. -.. method:: Trace.runctx(cmd[, globals=None[, locals=None]]) +.. method:: Trace.runctx(cmd, globals=None, locals=None) - Run *cmd* under control of the Trace object with the current tracing parameters + Run *cmd* under control of the :class:`Trace` object with the current tracing parameters in the defined global and local environments. If not defined, *globals* and *locals* default to empty dictionaries. @@ -110,7 +151,31 @@ Call *func* with the given arguments under control of the :class:`Trace` object with the current tracing parameters. -This is a simple example showing the use of this module:: +.. method:: Trace.results() + + Return a :class:`CoverageResults` object that contains the cumulative results + of all previous calls to ``run``, ``runctx`` and ``runfunc`` for the given + :class:`Trace` instance. Does not reset the accumulated trace results. + +.. class:: CoverageResults + + A container for coverage results, created by :meth:`Trace.results`. Should not + be created directly by the user. + +.. method:: CoverageResults.update(other) + + Merge in data from another :class:`CoverageResults` object. + +.. method:: CoverageResults.write_results(show_missing=True, summary=False, coverdir=None) + + Write coverage results. Set *show_missing* to show lines that had no hits. + Set *summary* to include in the output the coverage summary per module. *coverdir* + specifies the directory into which the coverage result files will be output. + If ``None``, the results for each source file are placed in its directory. + +.. + +A simple example demonstrating the use of the programming interface:: import sys import trace From python-checkins at python.org Mon Aug 2 21:32:43 2010 From: python-checkins at python.org (georg.brandl) Date: Mon, 2 Aug 2010 21:32:43 +0200 (CEST) Subject: [Python-checkins] r83550 - python/branches/py3k/Doc/reference/lexical_analysis.rst Message-ID: <20100802193243.44CD9EE9B9@mail.python.org> Author: georg.brandl Date: Mon Aug 2 21:32:43 2010 New Revision: 83550 Log: #9451: strengthen warning about __*__ special name usage. Modified: python/branches/py3k/Doc/reference/lexical_analysis.rst Modified: python/branches/py3k/Doc/reference/lexical_analysis.rst ============================================================================== --- python/branches/py3k/Doc/reference/lexical_analysis.rst (original) +++ python/branches/py3k/Doc/reference/lexical_analysis.rst Mon Aug 2 21:32:43 2010 @@ -362,11 +362,12 @@ information on this convention. ``__*__`` - System-defined names. These names are defined by the interpreter and its - implementation (including the standard library); applications should not expect - to define additional names using this convention. The set of names of this - class defined by Python may be extended in future versions. See section - :ref:`specialnames`. + System-defined names. These names are defined by the interpreter and its + implementation (including the standard library). Current system names are + discussed in the :ref:`specialnames` section and elsewhere. More will likely + be defined in future versions of Python. *Any* use of ``__*__`` names, in + any context, that does not follow explicitly documented use, is subject to + breakage without warning. ``__*`` Class-private names. Names in this category, when used within the context of a From python-checkins at python.org Mon Aug 2 21:35:07 2010 From: python-checkins at python.org (georg.brandl) Date: Mon, 2 Aug 2010 21:35:07 +0200 (CEST) Subject: [Python-checkins] r83551 - python/branches/py3k/Doc/library/constants.rst Message-ID: <20100802193507.1F294EEB9C@mail.python.org> Author: georg.brandl Date: Mon Aug 2 21:35:06 2010 New Revision: 83551 Log: Remove XXX comment that was displayed. Modified: python/branches/py3k/Doc/library/constants.rst Modified: python/branches/py3k/Doc/library/constants.rst ============================================================================== --- python/branches/py3k/Doc/library/constants.rst (original) +++ python/branches/py3k/Doc/library/constants.rst Mon Aug 2 21:35:06 2010 @@ -40,10 +40,8 @@ .. data:: Ellipsis - The same as ``...``. Special value used mostly in conjunction with extended - slicing syntax for user-defined container data types, as in :: - - .. XXX Someone who understands extended slicing should fill in here. + The same as ``...``. Special value used mostly in conjunction with extended + slicing syntax for user-defined container data types. .. data:: __debug__ From python-checkins at python.org Mon Aug 2 21:36:36 2010 From: python-checkins at python.org (georg.brandl) Date: Mon, 2 Aug 2010 21:36:36 +0200 (CEST) Subject: [Python-checkins] r83552 - python/branches/py3k/Doc/library/constants.rst Message-ID: <20100802193636.C4CBCEEAD8@mail.python.org> Author: georg.brandl Date: Mon Aug 2 21:36:36 2010 New Revision: 83552 Log: #9438: clarify that constant names also cannot be assigned as attributes. Modified: python/branches/py3k/Doc/library/constants.rst Modified: python/branches/py3k/Doc/library/constants.rst ============================================================================== --- python/branches/py3k/Doc/library/constants.rst (original) +++ python/branches/py3k/Doc/library/constants.rst Mon Aug 2 21:36:36 2010 @@ -3,15 +3,6 @@ A small number of constants live in the built-in namespace. They are: - -.. note:: - - :data:`None`, :data:`False`, :data:`True` and :data:`__debug__` cannot be - reassigned (assignments to them raise :exc:`SyntaxError`), so they can be - considered "true" constants. - -.. XXX False, True, None are keywords too - .. data:: False The false value of the :class:`bool` type. Assignments to ``False`` @@ -50,6 +41,12 @@ Assignments to :const:`__debug__` are illegal and raise a :exc:`SyntaxError`. See also the :keyword:`assert` statement. +.. note:: + + The names :data:`None`, :data:`False`, :data:`True` and :data:`__debug__` + cannot be reassigned (assignments to them, even as an attribute name, raise + :exc:`SyntaxError`), so they can be considered "true" constants. + Constants added by the :mod:`site` module ----------------------------------------- From python-checkins at python.org Mon Aug 2 21:39:17 2010 From: python-checkins at python.org (georg.brandl) Date: Mon, 2 Aug 2010 21:39:17 +0200 (CEST) Subject: [Python-checkins] r83553 - python/branches/py3k/Doc/library/constants.rst Message-ID: <20100802193917.4A6D9EEB6D@mail.python.org> Author: georg.brandl Date: Mon Aug 2 21:39:17 2010 New Revision: 83553 Log: Remove redundant information. Modified: python/branches/py3k/Doc/library/constants.rst Modified: python/branches/py3k/Doc/library/constants.rst ============================================================================== --- python/branches/py3k/Doc/library/constants.rst (original) +++ python/branches/py3k/Doc/library/constants.rst Mon Aug 2 21:39:17 2010 @@ -38,9 +38,9 @@ .. data:: __debug__ This constant is true if Python was not started with an :option:`-O` option. - Assignments to :const:`__debug__` are illegal and raise a :exc:`SyntaxError`. See also the :keyword:`assert` statement. + .. note:: The names :data:`None`, :data:`False`, :data:`True` and :data:`__debug__` From python-checkins at python.org Mon Aug 2 21:43:05 2010 From: python-checkins at python.org (georg.brandl) Date: Mon, 2 Aug 2010 21:43:05 +0200 (CEST) Subject: [Python-checkins] r83554 - python/branches/py3k/PCbuild/readme.txt Message-ID: <20100802194305.F1D61EEB1A@mail.python.org> Author: georg.brandl Date: Mon Aug 2 21:43:05 2010 New Revision: 83554 Log: #7280: note about nasmw.exe. Modified: python/branches/py3k/PCbuild/readme.txt Modified: python/branches/py3k/PCbuild/readme.txt ============================================================================== --- python/branches/py3k/PCbuild/readme.txt (original) +++ python/branches/py3k/PCbuild/readme.txt Mon Aug 2 21:43:05 2010 @@ -152,6 +152,8 @@ You must install the NASM assembler from http://nasm.sf.net for x86 builds. Put nasmw.exe anywhere in your PATH. + Note: recent releases of nasm only have nasm.exe. Just rename it to + nasmw.exe. You can also install ActivePerl from http://www.activestate.com/Products/ActivePerl/ From python-checkins at python.org Mon Aug 2 21:44:48 2010 From: python-checkins at python.org (georg.brandl) Date: Mon, 2 Aug 2010 21:44:48 +0200 (CEST) Subject: [Python-checkins] r83555 - python/branches/py3k/Lib/curses/wrapper.py Message-ID: <20100802194448.BAF70EEB9C@mail.python.org> Author: georg.brandl Date: Mon Aug 2 21:44:48 2010 New Revision: 83555 Log: #8861: remove unused variable. Modified: python/branches/py3k/Lib/curses/wrapper.py Modified: python/branches/py3k/Lib/curses/wrapper.py ============================================================================== --- python/branches/py3k/Lib/curses/wrapper.py (original) +++ python/branches/py3k/Lib/curses/wrapper.py Mon Aug 2 21:44:48 2010 @@ -17,10 +17,9 @@ wrapper(). """ - res = None try: # Initialize curses - stdscr=curses.initscr() + stdscr = curses.initscr() # Turn off echoing of keys, and enter cbreak mode, # where no buffering is performed on keyboard input From python-checkins at python.org Mon Aug 2 21:50:17 2010 From: python-checkins at python.org (georg.brandl) Date: Mon, 2 Aug 2010 21:50:17 +0200 (CEST) Subject: [Python-checkins] r83556 - peps/trunk/pep-0278.txt Message-ID: <20100802195017.0D6D0F503@mail.python.org> Author: georg.brandl Date: Mon Aug 2 21:50:16 2010 New Revision: 83556 Log: #8562: add note about outdated check. Modified: peps/trunk/pep-0278.txt Modified: peps/trunk/pep-0278.txt ============================================================================== --- peps/trunk/pep-0278.txt (original) +++ peps/trunk/pep-0278.txt Mon Aug 2 21:50:16 2010 @@ -179,6 +179,10 @@ namespace in the same release as the universal newline feature was added). + Additionally, note that this test fails again on Python versions + >= 2.5, when open() was made a function again and is not synonymous + with the file type anymore. + Reference Implementation From python-checkins at python.org Mon Aug 2 21:56:06 2010 From: python-checkins at python.org (ezio.melotti) Date: Mon, 2 Aug 2010 21:56:06 +0200 (CEST) Subject: [Python-checkins] r83557 - in python/branches/release26-maint: Lib/test/list_tests.py Lib/test/mapping_tests.py Lib/test/test_StringIO.py Lib/test/test_array.py Lib/test/test_ast.py Lib/test/test_augassign.py Lib/test/test_bigmem.py Lib/test/test_bool.py Lib/test/test_buffer.py Lib/test/test_builtin.py Lib/test/test_call.py Lib/test/test_class.py Lib/test/test_ctypes.py Lib/test/test_descr.py Lib/test/test_doctest.py Lib/test/test_heapq.py Lib/test/test_inspect.py Lib/test/test_iter.py Lib/test/test_long.py Lib/test/test_marshal.py Lib/test/test_multiprocessing.py Lib/test/test_operator.py Lib/test/test_peepholer.py Lib/test/test_richcmp.py Lib/test/test_set.py Lib/test/test_sets.py Lib/test/test_slice.py Lib/test/test_socket.py Lib/test/test_sort.py Lib/test/test_ssl.py Lib/test/test_syntax.py Lib/test/test_types.py Lib/test/test_undocumented_details.py Lib/test/test_userdict.py Lib/test/test_userlist.py Lib/test/test_weakref.py Lib/test/test_zipimport_support.py Message-ID: <20100802195606.7EDC3EEBB3@mail.python.org> Author: ezio.melotti Date: Mon Aug 2 21:56:05 2010 New Revision: 83557 Log: Merged revisions 79165 via svnmerge from svn+ssh://pythondev at svn.python.org/python/trunk ........ r79165 | florent.xicluna | 2010-03-21 03:14:24 +0200 (Sun, 21 Mar 2010) | 2 lines #7092 - Silence more py3k deprecation warnings, using test_support.check_py3k_warnings() helper. ........ Modified: python/branches/release26-maint/ (props changed) python/branches/release26-maint/Lib/test/list_tests.py python/branches/release26-maint/Lib/test/mapping_tests.py python/branches/release26-maint/Lib/test/test_StringIO.py python/branches/release26-maint/Lib/test/test_array.py python/branches/release26-maint/Lib/test/test_ast.py python/branches/release26-maint/Lib/test/test_augassign.py python/branches/release26-maint/Lib/test/test_bigmem.py python/branches/release26-maint/Lib/test/test_bool.py python/branches/release26-maint/Lib/test/test_buffer.py python/branches/release26-maint/Lib/test/test_builtin.py python/branches/release26-maint/Lib/test/test_call.py python/branches/release26-maint/Lib/test/test_class.py python/branches/release26-maint/Lib/test/test_ctypes.py python/branches/release26-maint/Lib/test/test_descr.py python/branches/release26-maint/Lib/test/test_doctest.py python/branches/release26-maint/Lib/test/test_heapq.py python/branches/release26-maint/Lib/test/test_inspect.py python/branches/release26-maint/Lib/test/test_iter.py python/branches/release26-maint/Lib/test/test_long.py python/branches/release26-maint/Lib/test/test_marshal.py python/branches/release26-maint/Lib/test/test_multiprocessing.py python/branches/release26-maint/Lib/test/test_operator.py python/branches/release26-maint/Lib/test/test_peepholer.py python/branches/release26-maint/Lib/test/test_richcmp.py python/branches/release26-maint/Lib/test/test_set.py python/branches/release26-maint/Lib/test/test_sets.py python/branches/release26-maint/Lib/test/test_slice.py python/branches/release26-maint/Lib/test/test_socket.py python/branches/release26-maint/Lib/test/test_sort.py python/branches/release26-maint/Lib/test/test_ssl.py python/branches/release26-maint/Lib/test/test_syntax.py python/branches/release26-maint/Lib/test/test_types.py python/branches/release26-maint/Lib/test/test_undocumented_details.py python/branches/release26-maint/Lib/test/test_userdict.py python/branches/release26-maint/Lib/test/test_userlist.py python/branches/release26-maint/Lib/test/test_weakref.py python/branches/release26-maint/Lib/test/test_zipimport_support.py Modified: python/branches/release26-maint/Lib/test/list_tests.py ============================================================================== --- python/branches/release26-maint/Lib/test/list_tests.py (original) +++ python/branches/release26-maint/Lib/test/list_tests.py Mon Aug 2 21:56:05 2010 @@ -36,7 +36,7 @@ self.assertEqual(str(a0), str(l0)) self.assertEqual(repr(a0), repr(l0)) - self.assertEqual(`a2`, `l2`) + self.assertEqual(repr(a2), repr(l2)) self.assertEqual(str(a2), "[0, 1, 2]") self.assertEqual(repr(a2), "[0, 1, 2]") @@ -419,6 +419,11 @@ self.assertRaises(TypeError, u.reverse, 42) def test_sort(self): + with test_support._check_py3k_warnings( + ("the cmp argument is not supported", DeprecationWarning)): + self._test_sort() + + def _test_sort(self): u = self.type2test([1, 0]) u.sort() self.assertEqual(u, [0, 1]) Modified: python/branches/release26-maint/Lib/test/mapping_tests.py ============================================================================== --- python/branches/release26-maint/Lib/test/mapping_tests.py (original) +++ python/branches/release26-maint/Lib/test/mapping_tests.py Mon Aug 2 21:56:05 2010 @@ -1,6 +1,7 @@ # tests common to dict and UserDict import unittest import UserDict +import test_support class BasicTestMappingProtocol(unittest.TestCase): @@ -54,13 +55,17 @@ #len self.assertEqual(len(p), 0) self.assertEqual(len(d), len(self.reference)) - #has_key + #in for k in self.reference: - self.assert_(d.has_key(k)) - self.assert_(k in d) + self.assertTrue(k in d) for k in self.other: - self.failIf(d.has_key(k)) - self.failIf(k in d) + self.assertTrue(k not in d) + #has_key + with test_support._check_py3k_warnings(quiet=True): + for k in self.reference: + self.assertTrue(d.has_key(k)) + for k in self.other: + self.assertFalse(d.has_key(k)) #cmp self.assertEqual(cmp(p,p), 0) self.assertEqual(cmp(d,d), 0) Modified: python/branches/release26-maint/Lib/test/test_StringIO.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_StringIO.py (original) +++ python/branches/release26-maint/Lib/test/test_StringIO.py Mon Aug 2 21:56:05 2010 @@ -137,12 +137,10 @@ def test_main(): - test_support.run_unittest( - TestStringIO, - TestcStringIO, - TestBufferStringIO, - TestBuffercStringIO - ) + test_support.run_unittest(TestStringIO, TestcStringIO) + with test_support._check_py3k_warnings(("buffer.. not supported", + DeprecationWarning)): + test_support.run_unittest(TestBufferStringIO, TestBuffercStringIO) if __name__ == '__main__': test_main() Modified: python/branches/release26-maint/Lib/test/test_array.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_array.py (original) +++ python/branches/release26-maint/Lib/test/test_array.py Mon Aug 2 21:56:05 2010 @@ -728,7 +728,8 @@ def test_buffer(self): a = array.array(self.typecode, self.example) - b = buffer(a) + with test_support._check_py3k_warnings(): + b = buffer(a) self.assertEqual(b[0], a.tostring()[0]) def test_weakref(self): Modified: python/branches/release26-maint/Lib/test/test_ast.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_ast.py (original) +++ python/branches/release26-maint/Lib/test/test_ast.py Mon Aug 2 21:56:05 2010 @@ -272,7 +272,9 @@ self.assertRaises(ValueError, ast.literal_eval, 'foo()') def test_main(): - test_support.run_unittest(AST_Tests, ASTHelpers_Test) + with test_support._check_py3k_warnings(("backquote not supported", + SyntaxWarning)): + test_support.run_unittest(AST_Tests, ASTHelpers_Test) def main(): if __name__ != '__main__': Modified: python/branches/release26-maint/Lib/test/test_augassign.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_augassign.py (original) +++ python/branches/release26-maint/Lib/test/test_augassign.py Mon Aug 2 21:56:05 2010 @@ -1,6 +1,6 @@ # Augmented assignment test. -from test.test_support import run_unittest +from test.test_support import run_unittest, _check_py3k_warnings import unittest @@ -321,7 +321,8 @@ '''.splitlines()) def test_main(): - run_unittest(AugAssignTest) + with _check_py3k_warnings(("classic int division", DeprecationWarning)): + run_unittest(AugAssignTest) if __name__ == '__main__': test_main() Modified: python/branches/release26-maint/Lib/test/test_bigmem.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_bigmem.py (original) +++ python/branches/release26-maint/Lib/test/test_bigmem.py Mon Aug 2 21:56:05 2010 @@ -97,21 +97,21 @@ def test_encode(self, size): return self.basic_encode_test(size, 'utf-8') - @precisionbigmemtest(size=_4G / 6 + 2, memuse=2) + @precisionbigmemtest(size=_4G // 6 + 2, memuse=2) def test_encode_raw_unicode_escape(self, size): try: return self.basic_encode_test(size, 'raw_unicode_escape') except MemoryError: pass # acceptable on 32-bit - @precisionbigmemtest(size=_4G / 5 + 70, memuse=3) + @precisionbigmemtest(size=_4G // 5 + 70, memuse=3) def test_encode_utf7(self, size): try: return self.basic_encode_test(size, 'utf7') except MemoryError: pass # acceptable on 32-bit - @precisionbigmemtest(size=_4G / 4 + 5, memuse=6) + @precisionbigmemtest(size=_4G // 4 + 5, memuse=6) def test_encode_utf32(self, size): try: return self.basic_encode_test(size, 'utf32', expectedsize=4*size+4) @@ -122,7 +122,7 @@ def test_decodeascii(self, size): return self.basic_encode_test(size, 'ascii', c='A') - @precisionbigmemtest(size=_4G / 5, memuse=6+2) + @precisionbigmemtest(size=_4G // 5, memuse=6+2) def test_unicode_repr_oflw(self, size): try: s = u"\uAAAA"*size @@ -516,7 +516,7 @@ self.assertEquals(s.count('\\'), size) self.assertEquals(s.count('0'), size * 2) - @bigmemtest(minsize=2**32 / 5, memuse=6+2) + @bigmemtest(minsize=2**32 // 5, memuse=6+2) def test_unicode_repr(self, size): s = u"\uAAAA" * size self.failUnless(len(repr(s)) > size) @@ -1053,7 +1053,8 @@ @precisionbigmemtest(size=_1G, memuse=4) def test_repeat(self, size): try: - b = buffer("AAAA")*size + with test_support._check_py3k_warnings(): + b = buffer("AAAA")*size except MemoryError: pass # acceptable on 32-bit else: Modified: python/branches/release26-maint/Lib/test/test_bool.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_bool.py (original) +++ python/branches/release26-maint/Lib/test/test_bool.py Mon Aug 2 21:56:05 2010 @@ -91,10 +91,10 @@ self.assertEqual(False*1, 0) self.assertIsNot(False*1, False) - self.assertEqual(True/1, 1) - self.assertIsNot(True/1, True) - self.assertEqual(False/1, 0) - self.assertIsNot(False/1, False) + self.assertEqual(True//1, 1) + self.assertIsNot(True//1, True) + self.assertEqual(False//1, 0) + self.assertIsNot(False//1, False) for b in False, True: for i in 0, 1, 2: @@ -168,8 +168,9 @@ self.assertIs(hasattr([], "wobble"), False) def test_callable(self): - self.assertIs(callable(len), True) - self.assertIs(callable(1), False) + with test_support._check_py3k_warnings(): + self.assertIs(callable(len), True) + self.assertIs(callable(1), False) def test_isinstance(self): self.assertIs(isinstance(True, bool), True) @@ -184,8 +185,11 @@ self.assertIs(issubclass(int, bool), False) def test_haskey(self): - self.assertIs({}.has_key(1), False) - self.assertIs({1:1}.has_key(1), True) + self.assertIs(1 in {}, False) + self.assertIs(1 in {1:1}, True) + with test_support._check_py3k_warnings(): + self.assertIs({}.has_key(1), False) + self.assertIs({1:1}.has_key(1), True) def test_string(self): self.assertIs("xyz".endswith("z"), True) @@ -257,8 +261,9 @@ import operator self.assertIs(operator.truth(0), False) self.assertIs(operator.truth(1), True) - self.assertIs(operator.isCallable(0), False) - self.assertIs(operator.isCallable(len), True) + with test_support._check_py3k_warnings(): + self.assertIs(operator.isCallable(0), False) + self.assertIs(operator.isCallable(len), True) self.assertIs(operator.isNumberType(None), False) self.assertIs(operator.isNumberType(0), True) self.assertIs(operator.not_(1), False) Modified: python/branches/release26-maint/Lib/test/test_buffer.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_buffer.py (original) +++ python/branches/release26-maint/Lib/test/test_buffer.py Mon Aug 2 21:56:05 2010 @@ -23,7 +23,9 @@ def test_main(): - test_support.run_unittest(BufferTests) + with test_support._check_py3k_warnings(("buffer.. not supported", + DeprecationWarning)): + test_support.run_unittest(BufferTests) if __name__ == "__main__": test_main() Modified: python/branches/release26-maint/Lib/test/test_builtin.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_builtin.py (original) +++ python/branches/release26-maint/Lib/test/test_builtin.py Mon Aug 2 21:56:05 2010 @@ -1,17 +1,13 @@ # Python test set -- built-in functions import platform -import test.test_support, unittest -from test.test_support import fcmp, have_unicode, TESTFN, unlink, \ - run_unittest, run_with_locale, check_warnings +import unittest +import warnings +from test.test_support import (fcmp, have_unicode, TESTFN, unlink, + run_unittest, _check_py3k_warnings, check_warnings) from operator import neg -import sys, warnings, cStringIO, random, fractions, UserDict -warnings.filterwarnings("ignore", "hex../oct.. of negative int", - FutureWarning, __name__) -warnings.filterwarnings("ignore", "integer argument expected", - DeprecationWarning, "unittest") - +import sys, cStringIO, random, UserDict # count the number of test runs. # used to skip running test_execfile() multiple times # and to create unique strings to intern in test_intern() @@ -419,7 +415,9 @@ f.write('z = z+1\n') f.write('z = z*2\n') f.close() - execfile(TESTFN) + with _check_py3k_warnings(("execfile.. not supported in 3.x", + DeprecationWarning)): + execfile(TESTFN) def test_execfile(self): global numruns @@ -1073,7 +1071,10 @@ # Reject floats when it would require PyLongs to represent. # (smaller floats still accepted, but deprecated) - self.assertRaises(TypeError, range, 1e100, 1e101, 1e101) + with check_warnings() as w: + warnings.simplefilter("always") + self.assertRaises(TypeError, range, 1e100, 1e101, 1e101) + self.assertEqual(w.category, DeprecationWarning) with check_warnings() as w: warnings.simplefilter("always") self.assertEqual(range(1.0), [0]) @@ -1119,19 +1120,20 @@ # Exercise various combinations of bad arguments, to check # refcounting logic - self.assertRaises(TypeError, range, 1e100) + with check_warnings(): + self.assertRaises(TypeError, range, 1e100) - self.assertRaises(TypeError, range, 0, 1e100) - self.assertRaises(TypeError, range, 1e100, 0) - self.assertRaises(TypeError, range, 1e100, 1e100) - - self.assertRaises(TypeError, range, 0, 0, 1e100) - self.assertRaises(TypeError, range, 0, 1e100, 1) - self.assertRaises(TypeError, range, 0, 1e100, 1e100) - self.assertRaises(TypeError, range, 1e100, 0, 1) - self.assertRaises(TypeError, range, 1e100, 0, 1e100) - self.assertRaises(TypeError, range, 1e100, 1e100, 1) - self.assertRaises(TypeError, range, 1e100, 1e100, 1e100) + self.assertRaises(TypeError, range, 0, 1e100) + self.assertRaises(TypeError, range, 1e100, 0) + self.assertRaises(TypeError, range, 1e100, 1e100) + + self.assertRaises(TypeError, range, 0, 0, 1e100) + self.assertRaises(TypeError, range, 0, 1e100, 1) + self.assertRaises(TypeError, range, 0, 1e100, 1e100) + self.assertRaises(TypeError, range, 1e100, 0, 1) + self.assertRaises(TypeError, range, 1e100, 0, 1e100) + self.assertRaises(TypeError, range, 1e100, 1e100, 1) + self.assertRaises(TypeError, range, 1e100, 1e100, 1e100) def test_input_and_raw_input(self): self.write_testfile() Modified: python/branches/release26-maint/Lib/test/test_call.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_call.py (original) +++ python/branches/release26-maint/Lib/test/test_call.py Mon Aug 2 21:56:05 2010 @@ -12,7 +12,8 @@ self.assertRaises(TypeError, {}.has_key) def test_varargs1(self): - {}.has_key(0) + with test_support._check_py3k_warnings(): + {}.has_key(0) def test_varargs2(self): self.assertRaises(TypeError, {}.has_key, 0, 1) @@ -24,11 +25,13 @@ pass def test_varargs1_ext(self): - {}.has_key(*(0,)) + with test_support._check_py3k_warnings(): + {}.has_key(*(0,)) def test_varargs2_ext(self): try: - {}.has_key(*(1, 2)) + with test_support._check_py3k_warnings(): + {}.has_key(*(1, 2)) except TypeError: pass else: Modified: python/branches/release26-maint/Lib/test/test_class.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_class.py (original) +++ python/branches/release26-maint/Lib/test/test_class.py Mon Aug 2 21:56:05 2010 @@ -407,7 +407,7 @@ self.assertCallStack([("__coerce__", (testme, 1)), ('__cmp__', (testme, 1))]) callLst[:] = [] - testme <> 1 # XXX kill this in py3k + eval('testme <> 1') # XXX kill this in py3k self.assertCallStack([("__coerce__", (testme, 1)), ('__cmp__', (testme, 1))]) callLst[:] = [] @@ -427,7 +427,7 @@ self.assertCallStack([("__coerce__", (testme, 1)), ('__cmp__', (1, testme))]) callLst[:] = [] - 1 <> testme + eval('1 <> testme') self.assertCallStack([("__coerce__", (testme, 1)), ('__cmp__', (1, testme))]) callLst[:] = [] @@ -616,7 +616,11 @@ hash(a.f) def test_main(): - test_support.run_unittest(ClassTests) + with test_support._check_py3k_warnings( + (".+__(get|set|del)slice__ has been removed", DeprecationWarning), + ("classic int division", DeprecationWarning), + ("<> not supported", DeprecationWarning)): + test_support.run_unittest(ClassTests) if __name__=='__main__': test_main() Modified: python/branches/release26-maint/Lib/test/test_ctypes.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_ctypes.py (original) +++ python/branches/release26-maint/Lib/test/test_ctypes.py Mon Aug 2 21:56:05 2010 @@ -1,12 +1,17 @@ import unittest -from test.test_support import run_unittest -import ctypes.test +from test.test_support import run_unittest, import_module, _check_py3k_warnings +#Skip tests if _ctypes module does not exist +import_module('_ctypes') + def test_main(): - skipped, testcases = ctypes.test.get_tests(ctypes.test, "test_*.py", verbosity=0) - suites = [unittest.makeSuite(t) for t in testcases] - run_unittest(unittest.TestSuite(suites)) + with _check_py3k_warnings(("buffer.. not supported", DeprecationWarning), + ("classic (int|long) division", DeprecationWarning)): + import ctypes.test + skipped, testcases = ctypes.test.get_tests(ctypes.test, "test_*.py", verbosity=0) + suites = [unittest.makeSuite(t) for t in testcases] + run_unittest(unittest.TestSuite(suites)) if __name__ == "__main__": test_main() Modified: python/branches/release26-maint/Lib/test/test_descr.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_descr.py (original) +++ python/branches/release26-maint/Lib/test/test_descr.py Mon Aug 2 21:56:05 2010 @@ -4433,9 +4433,14 @@ def test_main(): - # Run all local test cases, with PTypesLongInitTest first. - test_support.run_unittest(PTypesLongInitTest, OperatorsTest, - ClassPropertiesAndMethods, DictProxyTests) + with test_support._check_py3k_warnings( + ("classic (int|long) division", DeprecationWarning), + ("coerce.. not supported", DeprecationWarning), + ("Overriding __cmp__ ", DeprecationWarning), + (".+__(get|set|del)slice__ has been removed", DeprecationWarning)): + # Run all local test cases, with PTypesLongInitTest first. + test_support.run_unittest(PTypesLongInitTest, OperatorsTest, + ClassPropertiesAndMethods, DictProxyTests) if __name__ == "__main__": test_main() Modified: python/branches/release26-maint/Lib/test/test_doctest.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_doctest.py (original) +++ python/branches/release26-maint/Lib/test/test_doctest.py Mon Aug 2 21:56:05 2010 @@ -2222,7 +2222,7 @@ >>> doctest.master = None # Reset master. (Note: we'll be clearing doctest.master after each call to -`doctest.testfile`, to supress warnings about multiple tests with the +`doctest.testfile`, to suppress warnings about multiple tests with the same name.) Globals may be specified with the `globs` and `extraglobs` parameters: @@ -2386,12 +2386,6 @@ # that these use the deprecated doctest.Tester, so should go away (or # be rewritten) someday. -# Ignore all warnings about the use of class Tester in this module. -# Note that the name of this module may differ depending on how it's -# imported, so the use of __name__ is important. -warnings.filterwarnings("ignore", "class Tester", DeprecationWarning, - __name__, 0) - def old_test1(): r""" >>> from doctest import Tester >>> t = Tester(globs={'x': 42}, verbose=0) @@ -2515,9 +2509,16 @@ def test_main(): # Check the doctest cases in doctest itself: test_support.run_doctest(doctest, verbosity=True) - # Check the doctest cases defined here: + from test import test_doctest - test_support.run_doctest(test_doctest, verbosity=True) + with test_support._check_py3k_warnings( + ("backquote not supported", SyntaxWarning), + ("execfile.. not supported", DeprecationWarning)): + # Ignore all warnings about the use of class Tester in this module. + warnings.filterwarnings("ignore", "class Tester is deprecated", + DeprecationWarning) + # Check the doctest cases defined here: + test_support.run_doctest(test_doctest, verbosity=True) import trace, sys def test_coverage(coverdir): Modified: python/branches/release26-maint/Lib/test/test_heapq.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_heapq.py (original) +++ python/branches/release26-maint/Lib/test/test_heapq.py Mon Aug 2 21:56:05 2010 @@ -358,7 +358,10 @@ for f in (self.module.nlargest, self.module.nsmallest): for s in ("123", "", range(1000), ('do', 1.2), xrange(2000,2200,5)): for g in (G, I, Ig, L, R): - self.assertEqual(f(2, g(s)), f(2,s)) + with test_support._check_py3k_warnings( + ("comparing unequal types not supported", + DeprecationWarning), quiet=True): + self.assertEqual(f(2, g(s)), f(2,s)) self.assertEqual(f(2, S(s)), []) self.assertRaises(TypeError, f, 2, X(s)) self.assertRaises(TypeError, f, 2, N(s)) Modified: python/branches/release26-maint/Lib/test/test_inspect.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_inspect.py (original) +++ python/branches/release26-maint/Lib/test/test_inspect.py Mon Aug 2 21:56:05 2010 @@ -4,10 +4,13 @@ import inspect import datetime -from test.test_support import TESTFN, run_unittest +from test.test_support import run_unittest, _check_py3k_warnings -from test import inspect_fodder as mod -from test import inspect_fodder2 as mod2 +with _check_py3k_warnings( + ("tuple parameter unpacking has been removed", SyntaxWarning), + quiet=True): + from test import inspect_fodder as mod + from test import inspect_fodder2 as mod2 # Functions tested in this suite: # ismodule, isclass, ismethod, isfunction, istraceback, isframe, iscode, @@ -26,7 +29,7 @@ import __builtin__ try: - 1/0 + 1 // 0 except: tb = sys.exc_traceback @@ -361,11 +364,14 @@ self.assertArgSpecEquals(A.m, ['self']) def test_getargspec_sublistofone(self): - def sublistOfOne((foo,)): return 1 - self.assertArgSpecEquals(sublistOfOne, [['foo']]) + with _check_py3k_warnings( + ("tuple parameter unpacking has been removed", SyntaxWarning), + ("parenthesized argument names are invalid", SyntaxWarning)): + exec 'def sublistOfOne((foo,)): return 1' + self.assertArgSpecEquals(sublistOfOne, [['foo']]) - def fakeSublistOfOne((foo)): return 1 - self.assertArgSpecEquals(fakeSublistOfOne, ['foo']) + exec 'def fakeSublistOfOne((foo)): return 1' + self.assertArgSpecEquals(fakeSublistOfOne, ['foo']) def test_classify_oldstyle(self): class A: Modified: python/branches/release26-maint/Lib/test/test_iter.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_iter.py (original) +++ python/branches/release26-maint/Lib/test/test_iter.py Mon Aug 2 21:56:05 2010 @@ -1,7 +1,8 @@ # Test iterators. import unittest -from test.test_support import run_unittest, TESTFN, unlink, have_unicode +from test.test_support import run_unittest, TESTFN, unlink, have_unicode, \ + _check_py3k_warnings # Test result of triple loop (too big to inline) TRIPLETS = [(0, 0, 0), (0, 0, 1), (0, 0, 2), @@ -389,21 +390,24 @@ # Test map()'s use of iterators. def test_builtin_map(self): - self.assertEqual(map(None, SequenceClass(5)), range(5)) self.assertEqual(map(lambda x: x+1, SequenceClass(5)), range(1, 6)) d = {"one": 1, "two": 2, "three": 3} - self.assertEqual(map(None, d), d.keys()) self.assertEqual(map(lambda k, d=d: (k, d[k]), d), d.items()) dkeys = d.keys() expected = [(i < len(d) and dkeys[i] or None, i, i < len(d) and dkeys[i] or None) for i in range(5)] - self.assertEqual(map(None, d, - SequenceClass(5), - iter(d.iterkeys())), - expected) + + # Deprecated map(None, ...) + with _check_py3k_warnings(): + self.assertEqual(map(None, SequenceClass(5)), range(5)) + self.assertEqual(map(None, d), d.keys()) + self.assertEqual(map(None, d, + SequenceClass(5), + iter(d.iterkeys())), + expected) f = open(TESTFN, "w") try: @@ -499,7 +503,11 @@ self.assertEqual(zip(x, y), expected) # Test reduces()'s use of iterators. - def test_builtin_reduce(self): + def test_deprecated_builtin_reduce(self): + with _check_py3k_warnings(): + self._test_builtin_reduce() + + def _test_builtin_reduce(self): from operator import add self.assertEqual(reduce(add, SequenceClass(5)), 10) self.assertEqual(reduce(add, SequenceClass(5), 42), 52) Modified: python/branches/release26-maint/Lib/test/test_long.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_long.py (original) +++ python/branches/release26-maint/Lib/test/test_long.py Mon Aug 2 21:56:05 2010 @@ -561,11 +561,12 @@ def __getslice__(self, i, j): return i, j - self.assertEqual(X()[-5L:7L], (-5, 7)) - # use the clamping effect to test the smallest and largest longs - # that fit a Py_ssize_t - slicemin, slicemax = X()[-2L**100:2L**100] - self.assertEqual(X()[slicemin:slicemax], (slicemin, slicemax)) + with test_support._check_py3k_warnings(): + self.assertEqual(X()[-5L:7L], (-5, 7)) + # use the clamping effect to test the smallest and largest longs + # that fit a Py_ssize_t + slicemin, slicemax = X()[-2L**100:2L**100] + self.assertEqual(X()[slicemin:slicemax], (slicemin, slicemax)) # ----------------------------------- tests of auto int->long conversion @@ -605,8 +606,9 @@ checkit(x, '*', y) if y: - expected = longx / longy - got = x / y + with test_support._check_py3k_warnings(): + expected = longx / longy + got = x / y checkit(x, '/', y) expected = longx // longy Modified: python/branches/release26-maint/Lib/test/test_marshal.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_marshal.py (original) +++ python/branches/release26-maint/Lib/test/test_marshal.py Mon Aug 2 21:56:05 2010 @@ -129,7 +129,9 @@ def test_buffer(self): for s in ["", "Andr? Previn", "abc", " "*10000]: - b = buffer(s) + with test_support._check_py3k_warnings(("buffer.. not supported", + DeprecationWarning)): + b = buffer(s) new = marshal.loads(marshal.dumps(b)) self.assertEqual(s, new) marshal.dump(b, file(test_support.TESTFN, "wb")) Modified: python/branches/release26-maint/Lib/test/test_multiprocessing.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_multiprocessing.py (original) +++ python/branches/release26-maint/Lib/test/test_multiprocessing.py Mon Aug 2 21:56:05 2010 @@ -1934,7 +1934,9 @@ loadTestsFromTestCase = unittest.defaultTestLoader.loadTestsFromTestCase suite = unittest.TestSuite(loadTestsFromTestCase(tc) for tc in testcases) - run(suite) + with test_support._check_py3k_warnings( + (".+__(get|set)slice__ has been removed", DeprecationWarning)): + run(suite) ThreadsMixin.pool.terminate() ProcessesMixin.pool.terminate() Modified: python/branches/release26-maint/Lib/test/test_operator.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_operator.py (original) +++ python/branches/release26-maint/Lib/test/test_operator.py Mon Aug 2 21:56:05 2010 @@ -192,7 +192,9 @@ class C: pass def check(self, o, v): - self.assert_(operator.isCallable(o) == callable(o) == v) + with test_support._check_py3k_warnings(): + self.assertEqual(operator.isCallable(o), v) + self.assertEqual(callable(o), v) check(self, 4, 0) check(self, operator.isCallable, 1) check(self, C, 1) @@ -302,12 +304,13 @@ self.assertRaises(ValueError, operator.rshift, 2, -1) def test_contains(self): - self.failUnlessRaises(TypeError, operator.contains) - self.failUnlessRaises(TypeError, operator.contains, None, None) - self.failUnless(operator.contains(range(4), 2)) - self.failIf(operator.contains(range(4), 5)) - self.failUnless(operator.sequenceIncludes(range(4), 2)) - self.failIf(operator.sequenceIncludes(range(4), 5)) + self.assertRaises(TypeError, operator.contains) + self.assertRaises(TypeError, operator.contains, None, None) + self.assertTrue(operator.contains(range(4), 2)) + self.assertFalse(operator.contains(range(4), 5)) + with test_support._check_py3k_warnings(): + self.assertTrue(operator.sequenceIncludes(range(4), 2)) + self.assertFalse(operator.sequenceIncludes(range(4), 5)) def test_setitem(self): a = range(3) Modified: python/branches/release26-maint/Lib/test/test_peepholer.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_peepholer.py (original) +++ python/branches/release26-maint/Lib/test/test_peepholer.py Mon Aug 2 21:56:05 2010 @@ -207,17 +207,20 @@ import sys from test import test_support test_classes = (TestTranforms,) - test_support.run_unittest(*test_classes) - # verify reference counting - if verbose and hasattr(sys, "gettotalrefcount"): - import gc - counts = [None] * 5 - for i in xrange(len(counts)): - test_support.run_unittest(*test_classes) - gc.collect() - counts[i] = sys.gettotalrefcount() - print counts + with test_support._check_py3k_warnings( + ("backquote not supported", SyntaxWarning)): + test_support.run_unittest(*test_classes) + + # verify reference counting + if verbose and hasattr(sys, "gettotalrefcount"): + import gc + counts = [None] * 5 + for i in xrange(len(counts)): + test_support.run_unittest(*test_classes) + gc.collect() + counts[i] = sys.gettotalrefcount() + print counts if __name__ == "__main__": test_main(verbose=True) Modified: python/branches/release26-maint/Lib/test/test_richcmp.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_richcmp.py (original) +++ python/branches/release26-maint/Lib/test/test_richcmp.py Mon Aug 2 21:56:05 2010 @@ -330,7 +330,12 @@ self.assertIs(op(x, y), True) def test_main(): - test_support.run_unittest(VectorTest, NumberTest, MiscTest, DictTest, ListTest) + test_support.run_unittest(VectorTest, NumberTest, MiscTest, ListTest) + with test_support._check_py3k_warnings(("dict inequality comparisons " + "not supported in 3.x", + DeprecationWarning)): + test_support.run_unittest(DictTest) + if __name__ == "__main__": test_main() Modified: python/branches/release26-maint/Lib/test/test_set.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_set.py (original) +++ python/branches/release26-maint/Lib/test/test_set.py Mon Aug 2 21:56:05 2010 @@ -1348,6 +1348,10 @@ self.other = operator.add self.otherIsIterable = False + def test_ge_gt_le_lt(self): + with test_support._check_py3k_warnings(): + super(TestOnlySetsOperator, self).test_ge_gt_le_lt() + #------------------------------------------------------------------------------ class TestOnlySetsTuple(TestOnlySetsInBinaryOps): Modified: python/branches/release26-maint/Lib/test/test_sets.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_sets.py (original) +++ python/branches/release26-maint/Lib/test/test_sets.py Mon Aug 2 21:56:05 2010 @@ -1,13 +1,11 @@ #!/usr/bin/env python -import warnings -warnings.filterwarnings("ignore", "the sets module is deprecated", - DeprecationWarning, "test\.test_sets") - import unittest, operator, copy, pickle, random -from sets import Set, ImmutableSet from test import test_support +test_support.import_module("sets", deprecated=True) +from sets import Set, ImmutableSet + empty_set = Set() #============================================================================== @@ -638,6 +636,10 @@ self.other = operator.add self.otherIsIterable = False + def test_ge_gt_le_lt(self): + with test_support._check_py3k_warnings(): + super(TestOnlySetsOperator, self).test_ge_gt_le_lt() + #------------------------------------------------------------------------------ class TestOnlySetsTuple(TestOnlySetsInBinaryOps): @@ -679,11 +681,12 @@ def test_copy(self): dup = self.set.copy() - dup_list = list(dup); dup_list.sort() - set_list = list(self.set); set_list.sort() + self.assertEqual(len(dup), len(self.set)) + dup_list = sorted(dup) + set_list = sorted(self.set) self.assertEqual(len(dup_list), len(set_list)) - for i in range(len(dup_list)): - self.failUnless(dup_list[i] is set_list[i]) + for i, el in enumerate(dup_list): + self.assertTrue(el is set_list[i]) def test_deep_copy(self): dup = copy.deepcopy(self.set) @@ -694,6 +697,7 @@ for i in range(len(dup_list)): self.assertEqual(dup_list[i], set_list[i]) + #------------------------------------------------------------------------------ class TestCopyingEmpty(TestCopying): @@ -712,6 +716,10 @@ def setUp(self): self.set = Set(["zero", 0, None]) + def test_copy(self): + with test_support._check_py3k_warnings(): + super(TestCopyingTriple, self).test_copy() + #------------------------------------------------------------------------------ class TestCopyingTuple(TestCopying): Modified: python/branches/release26-maint/Lib/test/test_slice.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_slice.py (original) +++ python/branches/release26-maint/Lib/test/test_slice.py Mon Aug 2 21:56:05 2010 @@ -115,7 +115,8 @@ tmp.append((i, j, k)) x = X() - x[1:2] = 42 + with test_support._check_py3k_warnings(): + x[1:2] = 42 self.assertEquals(tmp, [(1, 2, 42)]) def test_pickle(self): Modified: python/branches/release26-maint/Lib/test/test_socket.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_socket.py (original) +++ python/branches/release26-maint/Lib/test/test_socket.py Mon Aug 2 21:56:05 2010 @@ -123,8 +123,9 @@ self.server_ready.wait() self.client_ready.set() self.clientSetUp() - if not callable(test_func): - raise TypeError, "test_func must be a callable function" + with test_support._check_py3k_warnings(): + if not callable(test_func): + raise TypeError("test_func must be a callable function.") try: test_func() except Exception, strerror: @@ -132,7 +133,7 @@ self.clientTearDown() def clientSetUp(self): - raise NotImplementedError, "clientSetUp must be implemented." + raise NotImplementedError("clientSetUp must be implemented.") def clientTearDown(self): self.done.set() @@ -282,8 +283,8 @@ orig = sys.getrefcount(__name__) socket.getnameinfo(__name__,0) except TypeError: - if sys.getrefcount(__name__) <> orig: - self.fail("socket.getnameinfo loses a reference") + self.assertEqual(sys.getrefcount(__name__), orig, + "socket.getnameinfo loses a reference") def testInterpreterCrash(self): # Making sure getnameinfo doesn't crash the interpreter @@ -1198,7 +1199,8 @@ self.assertEqual(msg, MSG) def _testRecvInto(self): - buf = buffer(MSG) + with test_support._check_py3k_warnings(): + buf = buffer(MSG) self.serv_conn.send(buf) def testRecvFromInto(self): @@ -1209,7 +1211,8 @@ self.assertEqual(msg, MSG) def _testRecvFromInto(self): - buf = buffer(MSG) + with test_support._check_py3k_warnings(): + buf = buffer(MSG) self.serv_conn.send(buf) Modified: python/branches/release26-maint/Lib/test/test_sort.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_sort.py (original) +++ python/branches/release26-maint/Lib/test/test_sort.py Mon Aug 2 21:56:05 2010 @@ -185,7 +185,7 @@ def test_stability(self): data = [(random.randrange(100), i) for i in xrange(200)] copy = data[:] - data.sort(key=lambda (x,y): x) # sort on the random first field + data.sort(key=lambda x: x[0]) # sort on the random first field copy.sort() # sort using both fields self.assertEqual(data, copy) # should get the same result @@ -207,7 +207,7 @@ # Verify that the wrapper has been removed data = range(-2,2) dup = data[:] - self.assertRaises(ZeroDivisionError, data.sort, None, lambda x: 1/x) + self.assertRaises(ZeroDivisionError, data.sort, None, lambda x: 1 // x) self.assertEqual(data, dup) def test_key_with_mutation(self): @@ -274,17 +274,19 @@ TestBugs, ) - test_support.run_unittest(*test_classes) - - # verify reference counting - if verbose and hasattr(sys, "gettotalrefcount"): - import gc - counts = [None] * 5 - for i in xrange(len(counts)): - test_support.run_unittest(*test_classes) - gc.collect() - counts[i] = sys.gettotalrefcount() - print counts + with test_support._check_py3k_warnings( + ("the cmp argument is not supported", DeprecationWarning)): + test_support.run_unittest(*test_classes) + + # verify reference counting + if verbose and hasattr(sys, "gettotalrefcount"): + import gc + counts = [None] * 5 + for i in xrange(len(counts)): + test_support.run_unittest(*test_classes) + gc.collect() + counts[i] = sys.gettotalrefcount() + print counts if __name__ == "__main__": test_main(verbose=True) Modified: python/branches/release26-maint/Lib/test/test_ssl.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_ssl.py (original) +++ python/branches/release26-maint/Lib/test/test_ssl.py Mon Aug 2 21:56:05 2010 @@ -975,7 +975,8 @@ # now fetch the same data from the HTTPS server url = 'https://127.0.0.1:%d/%s' % ( server.port, os.path.split(CERTFILE)[1]) - f = urllib.urlopen(url) + with test_support._check_py3k_warnings(): + f = urllib.urlopen(url) dlen = f.info().getheader("content-length") if dlen and (int(dlen) > 0): d2 = f.read(int(dlen)) Modified: python/branches/release26-maint/Lib/test/test_syntax.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_syntax.py (original) +++ python/branches/release26-maint/Lib/test/test_syntax.py Mon Aug 2 21:56:05 2010 @@ -506,7 +506,9 @@ def test_main(): test_support.run_unittest(SyntaxTestCase) from test import test_syntax - test_support.run_doctest(test_syntax, verbosity=True) + with test_support._check_py3k_warnings(("backquote not supported", + SyntaxWarning)): + test_support.run_doctest(test_syntax, verbosity=True) if __name__ == "__main__": test_main() Modified: python/branches/release26-maint/Lib/test/test_types.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_types.py (original) +++ python/branches/release26-maint/Lib/test/test_types.py Mon Aug 2 21:56:05 2010 @@ -1,6 +1,7 @@ # Python test set -- part 6, built-in types -from test.test_support import run_unittest, have_unicode, run_with_locale +from test.test_support import run_unittest, have_unicode, run_with_locale, \ + _check_py3k_warnings import unittest import sys import locale @@ -639,7 +640,10 @@ def test_main(): - run_unittest(TypesTests) + with _check_py3k_warnings( + ("buffer.. not supported", DeprecationWarning), + ("classic long division", DeprecationWarning)): + run_unittest(TypesTests) if __name__ == '__main__': test_main() Modified: python/branches/release26-maint/Lib/test/test_undocumented_details.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_undocumented_details.py (original) +++ python/branches/release26-maint/Lib/test/test_undocumented_details.py Mon Aug 2 21:56:05 2010 @@ -1,4 +1,4 @@ -from test.test_support import run_unittest, have_unicode +from test.test_support import run_unittest, _check_py3k_warnings import unittest import sys @@ -33,7 +33,8 @@ self.assertTrue(g_cell != h_cell) def test_main(): - run_unittest(TestImplementationComparisons) + with _check_py3k_warnings(): + run_unittest(TestImplementationComparisons) if __name__ == '__main__': test_main() Modified: python/branches/release26-maint/Lib/test/test_userdict.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_userdict.py (original) +++ python/branches/release26-maint/Lib/test/test_userdict.py Mon Aug 2 21:56:05 2010 @@ -45,7 +45,7 @@ # Test __repr__ self.assertEqual(str(u0), str(d0)) self.assertEqual(repr(u1), repr(d1)) - self.assertEqual(`u2`, `d2`) + self.assertEqual(repr(u2), repr(d2)) # Test __cmp__ and __len__ all = [d0, d1, d2, u, u0, u1, u2, uu, uu0, uu1, uu2] @@ -95,12 +95,13 @@ # Test has_key and "in". for i in u2.keys(): - self.assert_(u2.has_key(i)) - self.assert_(i in u2) - self.assertEqual(u1.has_key(i), d1.has_key(i)) + self.assertTrue(i in u2) self.assertEqual(i in u1, i in d1) - self.assertEqual(u0.has_key(i), d0.has_key(i)) self.assertEqual(i in u0, i in d0) + with test_support._check_py3k_warnings(): + self.assertTrue(u2.has_key(i)) + self.assertEqual(u1.has_key(i), d1.has_key(i)) + self.assertEqual(u0.has_key(i), d0.has_key(i)) # Test update t = UserDict.UserDict() Modified: python/branches/release26-maint/Lib/test/test_userlist.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_userlist.py (original) +++ python/branches/release26-maint/Lib/test/test_userlist.py Mon Aug 2 21:56:05 2010 @@ -53,7 +53,9 @@ self.assertEqual(iter(T((1,2))).next(), "0!!!") def test_main(): - test_support.run_unittest(UserListTest) + with test_support._check_py3k_warnings( + (".+__(get|set|del)slice__ has been removed", DeprecationWarning)): + test_support.run_unittest(UserListTest) if __name__ == "__main__": test_main() Modified: python/branches/release26-maint/Lib/test/test_weakref.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_weakref.py (original) +++ python/branches/release26-maint/Lib/test/test_weakref.py Mon Aug 2 21:56:05 2010 @@ -54,10 +54,10 @@ # Live reference: o = C() wr = weakref.ref(o) - `wr` + repr(wr) # Dead reference: del o - `wr` + repr(wr) def test_basic_callback(self): self.check_basic_callback(C) @@ -169,7 +169,8 @@ p.append(12) self.assertEqual(len(L), 1) self.failUnless(p, "proxy for non-empty UserList should be true") - p[:] = [2, 3] + with test_support._check_py3k_warnings(): + p[:] = [2, 3] self.assertEqual(len(L), 2) self.assertEqual(len(p), 2) self.failUnless(3 in p, @@ -183,10 +184,11 @@ ## self.assertEqual(repr(L2), repr(p2)) L3 = UserList.UserList(range(10)) p3 = weakref.proxy(L3) - self.assertEqual(L3[:], p3[:]) - self.assertEqual(L3[5:], p3[5:]) - self.assertEqual(L3[:5], p3[:5]) - self.assertEqual(L3[2:5], p3[2:5]) + with test_support._check_py3k_warnings(): + self.assertEqual(L3[:], p3[:]) + self.assertEqual(L3[5:], p3[5:]) + self.assertEqual(L3[:5], p3[:5]) + self.assertEqual(L3[2:5], p3[2:5]) def test_proxy_unicode(self): # See bug 5037 @@ -832,7 +834,7 @@ def test_weak_keys(self): # # This exercises d.copy(), d.items(), d[] = v, d[], del d[], - # len(d), d.has_key(). + # len(d), in d. # dict, objects = self.make_weak_keyed_dict() for o in objects: @@ -854,8 +856,8 @@ "deleting the keys did not clear the dictionary") o = Object(42) dict[o] = "What is the meaning of the universe?" - self.assert_(dict.has_key(o)) - self.assert_(not dict.has_key(34)) + self.assertTrue(o in dict) + self.assertTrue(34 not in dict) def test_weak_keyed_iters(self): dict, objects = self.make_weak_keyed_dict() @@ -867,8 +869,7 @@ objects2 = list(objects) for wr in refs: ob = wr() - self.assert_(dict.has_key(ob)) - self.assert_(ob in dict) + self.assertTrue(ob in dict) self.assertEqual(ob.arg, dict[ob]) objects2.remove(ob) self.assertEqual(len(objects2), 0) @@ -878,8 +879,7 @@ self.assertEqual(len(list(dict.iterkeyrefs())), len(objects)) for wr in dict.iterkeyrefs(): ob = wr() - self.assert_(dict.has_key(ob)) - self.assert_(ob in dict) + self.assertTrue(ob in dict) self.assertEqual(ob.arg, dict[ob]) objects2.remove(ob) self.assertEqual(len(objects2), 0) @@ -992,16 +992,16 @@ " -- value parameters must be distinct objects") weakdict = klass() o = weakdict.setdefault(key, value1) - self.assert_(o is value1) - self.assert_(weakdict.has_key(key)) - self.assert_(weakdict.get(key) is value1) - self.assert_(weakdict[key] is value1) + self.assertTrue(o is value1) + self.assertTrue(key in weakdict) + self.assertTrue(weakdict.get(key) is value1) + self.assertTrue(weakdict[key] is value1) o = weakdict.setdefault(key, value2) - self.assert_(o is value1) - self.assert_(weakdict.has_key(key)) - self.assert_(weakdict.get(key) is value1) - self.assert_(weakdict[key] is value1) + self.assertTrue(o is value1) + self.assertTrue(key in weakdict) + self.assertTrue(weakdict.get(key) is value1) + self.assertTrue(weakdict[key] is value1) def test_weak_valued_dict_setdefault(self): self.check_setdefault(weakref.WeakValueDictionary, @@ -1013,24 +1013,24 @@ def check_update(self, klass, dict): # - # This exercises d.update(), len(d), d.keys(), d.has_key(), + # This exercises d.update(), len(d), d.keys(), in d, # d.get(), d[]. # weakdict = klass() weakdict.update(dict) - self.assert_(len(weakdict) == len(dict)) + self.assertEqual(len(weakdict), len(dict)) for k in weakdict.keys(): - self.assert_(dict.has_key(k), + self.assertTrue(k in dict, "mysterious new key appeared in weak dict") v = dict.get(k) - self.assert_(v is weakdict[k]) - self.assert_(v is weakdict.get(k)) + self.assertTrue(v is weakdict[k]) + self.assertTrue(v is weakdict.get(k)) for k in dict.keys(): - self.assert_(weakdict.has_key(k), + self.assertTrue(k in weakdict, "original key disappeared in weak dict") v = dict[k] - self.assert_(v is weakdict[k]) - self.assert_(v is weakdict.get(k)) + self.assertTrue(v is weakdict[k]) + self.assertTrue(v is weakdict.get(k)) def test_weak_valued_dict_update(self): self.check_update(weakref.WeakValueDictionary, Modified: python/branches/release26-maint/Lib/test/test_zipimport_support.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_zipimport_support.py (original) +++ python/branches/release26-maint/Lib/test/test_zipimport_support.py Mon Aug 2 21:56:05 2010 @@ -14,6 +14,7 @@ import inspect import linecache import pdb +import warnings verbose = test.test_support.verbose @@ -170,8 +171,15 @@ test_zipped_doctest.test_testfile, test_zipped_doctest.test_unittest_reportflags, ] - for obj in known_good_tests: - _run_object_doctest(obj, test_zipped_doctest) + # Needed for test_DocTestParser and test_debug + with test.test_support._check_py3k_warnings( + ("backquote not supported", SyntaxWarning), + ("execfile.. not supported", DeprecationWarning)): + # Ignore all warnings about the use of class Tester in this module. + warnings.filterwarnings("ignore", "class Tester is deprecated", + DeprecationWarning) + for obj in known_good_tests: + _run_object_doctest(obj, test_zipped_doctest) def test_doctest_main_issue4197(self): test_src = textwrap.dedent("""\ From python-checkins at python.org Mon Aug 2 22:05:19 2010 From: python-checkins at python.org (georg.brandl) Date: Mon, 2 Aug 2010 22:05:19 +0200 (CEST) Subject: [Python-checkins] r83558 - python/branches/py3k/Doc/c-api/unicode.rst Message-ID: <20100802200519.360B8EEB35@mail.python.org> Author: georg.brandl Date: Mon Aug 2 22:05:19 2010 New Revision: 83558 Log: #8648: document UTF-7 codec functions. Modified: python/branches/py3k/Doc/c-api/unicode.rst Modified: python/branches/py3k/Doc/c-api/unicode.rst ============================================================================== --- python/branches/py3k/Doc/c-api/unicode.rst (original) +++ python/branches/py3k/Doc/c-api/unicode.rst Mon Aug 2 22:05:19 2010 @@ -670,6 +670,38 @@ Return *NULL* if an exception was raised by the codec. +UTF-7 Codecs +"""""""""""" + +These are the UTF-7 codec APIs: + + +.. cfunction:: PyObject* PyUnicode_DecodeUTF7(const char *s, Py_ssize_t size, const char *errors) + + Create a Unicode object by decoding *size* bytes of the UTF-7 encoded string + *s*. Return *NULL* if an exception was raised by the codec. + + +.. cfunction:: PyObject* PyUnicode_DecodeUTF8Stateful(const char *s, Py_ssize_t size, const char *errors, Py_ssize_t *consumed) + + If *consumed* is *NULL*, behave like :cfunc:`PyUnicode_DecodeUTF7`. If + *consumed* is not *NULL*, trailing incomplete UTF-7 base-64 sections will not + be treated as an error. Those bytes will not be decoded and the number of + bytes that have been decoded will be stored in *consumed*. + + +.. cfunction:: PyObject* PyUnicode_EncodeUTF7(const Py_UNICODE *s, Py_ssize_t size, int base64SetO, int base64WhiteSpace, const char *errors) + + Encode the :ctype:`Py_UNICODE` buffer of the given size using UTF-7 and + return a Python bytes object. Return *NULL* if an exception was raised by + the codec. + + If *base64SetO* is nonzero, "Set O" (punctuation that has no otherwise + special meaning) will be encoded in base-64. If *base64WhiteSpace* is + nonzero, whitespace will be encoded in base-64. Both are set to zero for the + Python "utf-7" codec. + + Unicode-Escape Codecs """"""""""""""""""""" From python-checkins at python.org Mon Aug 2 22:06:13 2010 From: python-checkins at python.org (ezio.melotti) Date: Mon, 2 Aug 2010 22:06:13 +0200 (CEST) Subject: [Python-checkins] r83559 - python/branches/release26-maint Message-ID: <20100802200613.72281EEAA7@mail.python.org> Author: ezio.melotti Date: Mon Aug 2 22:06:13 2010 New Revision: 83559 Log: Unblocked revisions 77912 via svnmerge ........ r77912 | ezio.melotti | 2010-02-02 17:57:45 +0200 (Tue, 02 Feb 2010) | 1 line Fix idioms and a couple of py3k warnings. Patch by Florent Xicluna. ........ Modified: python/branches/release26-maint/ (props changed) From python-checkins at python.org Mon Aug 2 22:16:18 2010 From: python-checkins at python.org (georg.brandl) Date: Mon, 2 Aug 2010 22:16:18 +0200 (CEST) Subject: [Python-checkins] r83560 - in python/branches/py3k: Lib/json/__init__.py Lib/json/decoder.py Lib/json/encoder.py Modules/_json.c Message-ID: <20100802201618.56C1AEEB9B@mail.python.org> Author: georg.brandl Date: Mon Aug 2 22:16:18 2010 New Revision: 83560 Log: #9087: update json docstrings -- unicode and long do not exist anymore. Modified: python/branches/py3k/Lib/json/__init__.py python/branches/py3k/Lib/json/decoder.py python/branches/py3k/Lib/json/encoder.py python/branches/py3k/Modules/_json.c Modified: python/branches/py3k/Lib/json/__init__.py ============================================================================== --- python/branches/py3k/Lib/json/__init__.py (original) +++ python/branches/py3k/Lib/json/__init__.py Mon Aug 2 22:16:18 2010 @@ -125,14 +125,12 @@ ``.write()``-supporting file-like object). If ``skipkeys`` is true then ``dict`` keys that are not basic types - (``str``, ``unicode``, ``int``, ``float``, ``bool``, ``None``) will be - skipped instead of raising a ``TypeError``. + (``str``, ``int``, ``float``, ``bool``, ``None``) will be skipped + instead of raising a ``TypeError``. - If ``ensure_ascii`` is false, then the some chunks written to ``fp`` - may be ``unicode`` instances, subject to normal Python ``str`` to - ``unicode`` coercion rules. Unless ``fp.write()`` explicitly - understands ``unicode`` (as in ``codecs.getwriter()``) this is likely - to cause an error. + If ``ensure_ascii`` is false, then the strings written to ``fp`` can + contain non-ASCII characters if they appear in strings contained in + ``obj``. Otherwise, all such characters are escaped in JSON strings. If ``check_circular`` is false, then the circular reference check for container types will be skipped and a circular reference will @@ -185,12 +183,12 @@ """Serialize ``obj`` to a JSON formatted ``str``. If ``skipkeys`` is false then ``dict`` keys that are not basic types - (``str``, ``unicode``, ``int``, ``float``, ``bool``, ``None``) will be - skipped instead of raising a ``TypeError``. + (``str``, ``int``, ``float``, ``bool``, ``None``) will be skipped + instead of raising a ``TypeError``. - If ``ensure_ascii`` is false, then the return value will be a - ``unicode`` instance subject to normal Python ``str`` to ``unicode`` - coercion rules instead of being escaped to an ASCII ``str``. + If ``ensure_ascii`` is false, then the return value can contain non-ASCII + characters if they appear in strings contained in ``obj``. Otherwise, all + such characters are escaped in JSON strings. If ``check_circular`` is false, then the circular reference check for container types will be skipped and a circular reference will Modified: python/branches/py3k/Lib/json/decoder.py ============================================================================== --- python/branches/py3k/Lib/json/decoder.py (original) +++ python/branches/py3k/Lib/json/decoder.py Mon Aug 2 22:16:18 2010 @@ -263,9 +263,9 @@ +---------------+-------------------+ | array | list | +---------------+-------------------+ - | string | unicode | + | string | str | +---------------+-------------------+ - | number (int) | int, long | + | number (int) | int | +---------------+-------------------+ | number (real) | float | +---------------+-------------------+ @@ -318,8 +318,8 @@ def decode(self, s, _w=WHITESPACE.match): - """Return the Python representation of ``s`` (a ``str`` or ``unicode`` - instance containing a JSON document) + """Return the Python representation of ``s`` (a ``str`` instance + containing a JSON document). """ obj, end = self.raw_decode(s, idx=_w(s, 0).end()) @@ -329,8 +329,8 @@ return obj def raw_decode(self, s, idx=0): - """Decode a JSON document from ``s`` (a ``str`` or ``unicode`` - beginning with a JSON document) and return a 2-tuple of the Python + """Decode a JSON document from ``s`` (a ``str`` beginning with + a JSON document) and return a 2-tuple of the Python representation and the index in ``s`` where the document ended. This can be used to decode a JSON document from a string that may Modified: python/branches/py3k/Lib/json/encoder.py ============================================================================== --- python/branches/py3k/Lib/json/encoder.py (original) +++ python/branches/py3k/Lib/json/encoder.py Mon Aug 2 22:16:18 2010 @@ -77,9 +77,9 @@ +-------------------+---------------+ | list, tuple | array | +-------------------+---------------+ - | str, unicode | string | + | str | string | +-------------------+---------------+ - | int, long, float | number | + | int, float | number | +-------------------+---------------+ | True | true | +-------------------+---------------+ @@ -102,12 +102,12 @@ """Constructor for JSONEncoder, with sensible defaults. If skipkeys is false, then it is a TypeError to attempt - encoding of keys that are not str, int, long, float or None. If + encoding of keys that are not str, int, float or None. If skipkeys is True, such items are simply skipped. If ensure_ascii is true, the output is guaranteed to be str - objects with all incoming unicode characters escaped. If - ensure_ascii is false, the output will be unicode object. + objects with all incoming non-ASCII characters escaped. If + ensure_ascii is false, the output can contain non-ASCII characters. If check_circular is true, then lists, dicts, and custom encoded objects will be checked for circular references during encoding to Modified: python/branches/py3k/Modules/_json.c ============================================================================== --- python/branches/py3k/Modules/_json.c (original) +++ python/branches/py3k/Modules/_json.c Mon Aug 2 22:16:18 2010 @@ -484,7 +484,7 @@ } PyDoc_STRVAR(pydoc_scanstring, - "scanstring(basestring, end, strict=True) -> (bytes, end)\n" + "scanstring(string, end, strict=True) -> (string, end)\n" "\n" "Scan the string s for a JSON string. End is the index of the\n" "character in s after the quote that started the JSON string.\n" @@ -512,7 +512,7 @@ } else { PyErr_Format(PyExc_TypeError, - "first argument must be a string or bytes, not %.80s", + "first argument must be a string, not %.80s", Py_TYPE(pystr)->tp_name); return NULL; } @@ -520,7 +520,7 @@ } PyDoc_STRVAR(pydoc_encode_basestring_ascii, - "encode_basestring_ascii(basestring) -> bytes\n" + "encode_basestring_ascii(string) -> string\n" "\n" "Return an ASCII-only JSON representation of a Python string" ); From python-checkins at python.org Mon Aug 2 22:17:50 2010 From: python-checkins at python.org (georg.brandl) Date: Mon, 2 Aug 2010 22:17:50 +0200 (CEST) Subject: [Python-checkins] r83561 - python/branches/py3k/Tools/versioncheck Message-ID: <20100802201750.A282EEEB9B@mail.python.org> Author: georg.brandl Date: Mon Aug 2 22:17:50 2010 New Revision: 83561 Log: #4280: remove outdated "versionchecker" tool. Removed: python/branches/py3k/Tools/versioncheck/ From python-checkins at python.org Mon Aug 2 22:19:22 2010 From: python-checkins at python.org (ezio.melotti) Date: Mon, 2 Aug 2010 22:19:22 +0200 (CEST) Subject: [Python-checkins] r83562 - in python/branches/release26-maint: Lib/bsddb/dbshelve.py Lib/bsddb/dbtables.py Lib/bsddb/test/test_all.py Lib/bsddb/test/test_basics.py Lib/bsddb/test/test_compare.py Lib/bsddb/test/test_dbtables.py Lib/bsddb/test/test_recno.py Lib/bsddb/test/test_replication.py Lib/bsddb/test/test_thread.py Lib/test/test_bsddb3.py Message-ID: <20100802201922.0595CEEA92@mail.python.org> Author: ezio.melotti Date: Mon Aug 2 22:19:21 2010 New Revision: 83562 Log: Merged revisions 77912 via svnmerge from svn+ssh://pythondev at svn.python.org/python/trunk ........ r77912 | ezio.melotti | 2010-02-02 17:57:45 +0200 (Tue, 02 Feb 2010) | 1 line Fix idioms and a couple of py3k warnings. Patch by Florent Xicluna. ........ Modified: python/branches/release26-maint/ (props changed) python/branches/release26-maint/Lib/bsddb/dbshelve.py python/branches/release26-maint/Lib/bsddb/dbtables.py python/branches/release26-maint/Lib/bsddb/test/test_all.py python/branches/release26-maint/Lib/bsddb/test/test_basics.py python/branches/release26-maint/Lib/bsddb/test/test_compare.py python/branches/release26-maint/Lib/bsddb/test/test_dbtables.py python/branches/release26-maint/Lib/bsddb/test/test_recno.py python/branches/release26-maint/Lib/bsddb/test/test_replication.py python/branches/release26-maint/Lib/bsddb/test/test_thread.py python/branches/release26-maint/Lib/test/test_bsddb3.py Modified: python/branches/release26-maint/Lib/bsddb/dbshelve.py ============================================================================== --- python/branches/release26-maint/Lib/bsddb/dbshelve.py (original) +++ python/branches/release26-maint/Lib/bsddb/dbshelve.py Mon Aug 2 22:19:21 2010 @@ -152,7 +152,7 @@ def keys(self, txn=None): - if txn != None: + if txn is not None: return self.db.keys(txn) else: return self.db.keys() @@ -176,7 +176,7 @@ def items(self, txn=None): - if txn != None: + if txn is not None: items = self.db.items(txn) else: items = self.db.items() @@ -187,7 +187,7 @@ return newitems def values(self, txn=None): - if txn != None: + if txn is not None: values = self.db.values(txn) else: values = self.db.values() Modified: python/branches/release26-maint/Lib/bsddb/dbtables.py ============================================================================== --- python/branches/release26-maint/Lib/bsddb/dbtables.py (original) +++ python/branches/release26-maint/Lib/bsddb/dbtables.py Mon Aug 2 22:19:21 2010 @@ -179,14 +179,14 @@ def set_range(self, search) : v = self._dbcursor.set_range(bytes(search, "iso8859-1")) - if v != None : + if v is not None : v = (v[0].decode("iso8859-1"), v[1].decode("iso8859-1")) return v def __next__(self) : v = getattr(self._dbcursor, "next")() - if v != None : + if v is not None : v = (v[0].decode("iso8859-1"), v[1].decode("iso8859-1")) return v @@ -204,7 +204,7 @@ def put(self, key, value, flags=0, txn=None) : key = bytes(key, "iso8859-1") - if value != None : + if value is not None : value = bytes(value, "iso8859-1") return self._db.put(key, value, flags=flags, txn=txn) @@ -215,7 +215,7 @@ def get(self, key, txn=None, flags=0) : key = bytes(key, "iso8859-1") v = self._db.get(key, txn=txn, flags=flags) - if v != None : + if v is not None : v = v.decode("iso8859-1") return v @@ -540,7 +540,7 @@ # error dataitem = None dataitem = mappings[column](dataitem) - if dataitem <> None: + if dataitem is not None: self.db.put( _data_key(table, column, rowid), dataitem, txn=txn) Modified: python/branches/release26-maint/Lib/bsddb/test/test_all.py ============================================================================== --- python/branches/release26-maint/Lib/bsddb/test/test_all.py (original) +++ python/branches/release26-maint/Lib/bsddb/test/test_all.py Mon Aug 2 22:19:21 2010 @@ -23,7 +23,7 @@ return getattr(self._dbcursor, v) def _fix(self, v) : - if v == None : return None + if v is None : return None key, value = v if isinstance(key, bytes) : key = key.decode(charset) @@ -90,7 +90,7 @@ def pget(self, key=None, data=None, flags=0) : # Incorrect because key can be a bare number, # but enough to pass testsuite - if isinstance(key, int) and (data==None) and (flags==0) : + if isinstance(key, int) and (data is None) and (flags == 0) : flags = key key = None if isinstance(key, str) : @@ -101,7 +101,7 @@ if isinstance(data, str) : data = bytes(data, charset) v=self._dbcursor.pget(key=key, data=data, flags=flags) - if v != None : + if v is not None : v1, v2, v3 = v if isinstance(v1, bytes) : v1 = v1.decode(charset) @@ -114,7 +114,7 @@ def join_item(self) : v = self._dbcursor.join_item() - if v != None : + if v is not None : v = v.decode(charset) return v @@ -134,7 +134,7 @@ args =(k, d, f) v = self._dbcursor.get(*args, **kwargs) - if v != None : + if v is not None : k, v = v if isinstance(k, bytes) : k = k.decode(charset) @@ -176,7 +176,7 @@ if isinstance(k, str) : k = bytes(k, charset) v = self._db[k] - if v != None : + if v is not None : v = v.decode(charset) return v @@ -230,7 +230,7 @@ else : v=self._db.get(key, txn=txn, flags=flags, dlen=dlen, doff=doff) - if (v != None) and isinstance(v, bytes) : + if (v is not None) and isinstance(v, bytes) : v = v.decode(charset) return v @@ -238,7 +238,7 @@ if isinstance(key, str) : key = bytes(key, charset) v=self._db.pget(key, txn=txn) - if v != None : + if v is not None : v1, v2 = v if isinstance(v1, bytes) : v1 = v1.decode(charset) @@ -252,7 +252,7 @@ if isinstance(value, str) : value = bytes(value, charset) v=self._db.get_both(key, value, txn=txn, flags=flags) - if v != None : + if v is not None : v = v.decode(charset) return v Modified: python/branches/release26-maint/Lib/bsddb/test/test_basics.py ============================================================================== --- python/branches/release26-maint/Lib/bsddb/test/test_basics.py (original) +++ python/branches/release26-maint/Lib/bsddb/test/test_basics.py Mon Aug 2 22:19:21 2010 @@ -342,7 +342,7 @@ else: if set_raises_error: self.fail("expected exception") - if n != None: + if n is not None: self.fail("expected None: %r" % (n,)) rec = c.get_both('0404', self.makeData('0404')) @@ -356,7 +356,7 @@ else: if get_raises_error: self.fail("expected exception") - if n != None: + if n is not None: self.fail("expected None: %r" % (n,)) if self.d.get_type() == db.DB_BTREE: Modified: python/branches/release26-maint/Lib/bsddb/test/test_compare.py ============================================================================== --- python/branches/release26-maint/Lib/bsddb/test/test_compare.py (original) +++ python/branches/release26-maint/Lib/bsddb/test/test_compare.py Mon Aug 2 22:19:21 2010 @@ -239,12 +239,7 @@ self.startTest () self.createDB (my_compare) - try: - self.db.set_bt_compare (my_compare) - self.assert_(0, "this set should fail") - - except RuntimeError, msg: - pass + self.assertRaises (RuntimeError, self.db.set_bt_compare, my_compare) def test_suite (): res = unittest.TestSuite () Modified: python/branches/release26-maint/Lib/bsddb/test/test_dbtables.py ============================================================================== --- python/branches/release26-maint/Lib/bsddb/test/test_dbtables.py (original) +++ python/branches/release26-maint/Lib/bsddb/test/test_dbtables.py Mon Aug 2 22:19:21 2010 @@ -341,7 +341,7 @@ self.tdb.Insert(tabname, {'Type': 'Unknown', 'Access': '0'}) def set_type(type): - if type == None: + if type is None: return 'MP3' return type Modified: python/branches/release26-maint/Lib/bsddb/test/test_recno.py ============================================================================== --- python/branches/release26-maint/Lib/bsddb/test/test_recno.py (original) +++ python/branches/release26-maint/Lib/bsddb/test/test_recno.py Mon Aug 2 22:19:21 2010 @@ -38,8 +38,8 @@ for x in letters: recno = d.append(x * 60) - self.assertEqual(type(recno), type(0)) - self.assert_(recno >= 1) + self.assertIsInstance(recno, int) + self.assertGreaterEqual(recno, 1) if verbose: print recno, @@ -54,7 +54,7 @@ if verbose: print data - self.assertEqual(type(data), type("")) + self.assertIsInstance(data, str) self.assertEqual(data, d.get(recno)) try: @@ -91,21 +91,21 @@ keys = d.keys() if verbose: print keys - self.assertEqual(type(keys), type([])) - self.assertEqual(type(keys[0]), type(123)) + self.assertIsInstance(keys, list) + self.assertIsInstance(keys[0], int) self.assertEqual(len(keys), len(d)) items = d.items() if verbose: pprint(items) - self.assertEqual(type(items), type([])) - self.assertEqual(type(items[0]), type(())) + self.assertIsInstance(items, list) + self.assertIsInstance(items[0], tuple) self.assertEqual(len(items[0]), 2) - self.assertEqual(type(items[0][0]), type(123)) - self.assertEqual(type(items[0][1]), type("")) + self.assertIsInstance(items[0][0], int) + self.assertIsInstance(items[0][1], str) self.assertEqual(len(items), len(d)) - self.assert_(d.has_key(25)) + self.assertTrue(d.has_key(25)) del d[25] self.assertFalse(d.has_key(25)) @@ -177,7 +177,7 @@ if get_returns_none: self.fail("unexpected DBKeyEmptyError exception") else: - self.assertEqual(val[0], db.DB_KEYEMPTY) + self.assertEqual(val.args[0], db.DB_KEYEMPTY) if verbose: print val else: if not get_returns_none: Modified: python/branches/release26-maint/Lib/bsddb/test/test_replication.py ============================================================================== --- python/branches/release26-maint/Lib/bsddb/test/test_replication.py (original) +++ python/branches/release26-maint/Lib/bsddb/test/test_replication.py Mon Aug 2 22:19:21 2010 @@ -186,11 +186,11 @@ import time timeout=time.time()+10 v=None - while (time.time() Author: georg.brandl Date: Mon Aug 2 22:21:21 2010 New Revision: 83563 Log: #9037: add example how to raise custom exceptions from C code. Modified: python/branches/py3k/Doc/extending/extending.rst Modified: python/branches/py3k/Doc/extending/extending.rst ============================================================================== --- python/branches/py3k/Doc/extending/extending.rst (original) +++ python/branches/py3k/Doc/extending/extending.rst Mon Aug 2 22:21:21 2010 @@ -226,9 +226,28 @@ become a dangling pointer. Should it become a dangling pointer, C code which raises the exception could cause a core dump or other unintended side effects. -We discuss the use of PyMODINIT_FUNC as a function return type later in this +We discuss the use of ``PyMODINIT_FUNC`` as a function return type later in this sample. +The :exc:`spam.error` exception can be raised in your extension module using a +call to :cfunc:`PyErr_SetString` as shown below:: + + static PyObject * + spam_system(PyObject *self, PyObject *args) + { + const char *command; + int sts; + + if (!PyArg_ParseTuple(args, "s", &command)) + return NULL; + sts = system(command); + if (sts < 0) { + PyErr_SetString(SpamError, "System command failed"); + return NULL; + } + return PyLong_FromLong(sts); + } + .. _backtoexample: From python-checkins at python.org Mon Aug 2 22:26:41 2010 From: python-checkins at python.org (ezio.melotti) Date: Mon, 2 Aug 2010 22:26:41 +0200 (CEST) Subject: [Python-checkins] r83564 - in python/branches/release26-maint: Lib/cgitb.py Lib/distutils/command/build_ext.py Lib/distutils/util.py Lib/idlelib/AutoCompleteWindow.py Lib/idlelib/MultiCall.py Lib/platform.py Message-ID: <20100802202641.E973EEEB84@mail.python.org> Author: ezio.melotti Date: Mon Aug 2 22:26:41 2010 New Revision: 83564 Log: Merged revisions 79558 via svnmerge from svn+ssh://pythondev at svn.python.org/python/trunk ........ r79558 | florent.xicluna | 2010-04-01 21:17:09 +0300 (Thu, 01 Apr 2010) | 2 lines #7092: Fix some -3 warnings, and fix Lib/platform.py when the path contains a double-quote. ........ Modified: python/branches/release26-maint/ (props changed) python/branches/release26-maint/Lib/cgitb.py python/branches/release26-maint/Lib/distutils/command/build_ext.py python/branches/release26-maint/Lib/distutils/util.py python/branches/release26-maint/Lib/idlelib/AutoCompleteWindow.py python/branches/release26-maint/Lib/idlelib/MultiCall.py python/branches/release26-maint/Lib/platform.py Modified: python/branches/release26-maint/Lib/cgitb.py ============================================================================== --- python/branches/release26-maint/Lib/cgitb.py (original) +++ python/branches/release26-maint/Lib/cgitb.py Mon Aug 2 22:26:41 2010 @@ -94,10 +94,10 @@ lasttoken = token return vars -def html((etype, evalue, etb), context=5): +def html(einfo, context=5): """Return a nice HTML document describing a given traceback.""" import os, types, time, traceback, linecache, inspect, pydoc - + etype, evalue, etb = einfo if type(etype) is types.ClassType: etype = etype.__name__ pyver = 'Python ' + sys.version.split()[0] + ': ' + sys.executable @@ -186,10 +186,10 @@ ''' % pydoc.html.escape( ''.join(traceback.format_exception(etype, evalue, etb))) -def text((etype, evalue, etb), context=5): +def text(einfo, context=5): """Return a plain text document describing a given traceback.""" import os, types, time, traceback, linecache, inspect, pydoc - + etype, evalue, etb = einfo if type(etype) is types.ClassType: etype = etype.__name__ pyver = 'Python ' + sys.version.split()[0] + ': ' + sys.executable Modified: python/branches/release26-maint/Lib/distutils/command/build_ext.py ============================================================================== --- python/branches/release26-maint/Lib/distutils/command/build_ext.py (original) +++ python/branches/release26-maint/Lib/distutils/command/build_ext.py Mon Aug 2 22:26:41 2010 @@ -676,7 +676,7 @@ # extensions in debug_mode are named 'module_d.pyd' under windows so_ext = get_config_var('SO') if os.name == 'nt' and self.debug: - return apply(os.path.join, ext_path) + '_d' + so_ext + return os.path.join(*ext_path) + '_d' + so_ext return os.path.join(*ext_path) + so_ext def get_export_symbols (self, ext): Modified: python/branches/release26-maint/Lib/distutils/util.py ============================================================================== --- python/branches/release26-maint/Lib/distutils/util.py (original) +++ python/branches/release26-maint/Lib/distutils/util.py Mon Aug 2 22:26:41 2010 @@ -205,7 +205,7 @@ paths.remove('.') if not paths: return os.curdir - return apply(os.path.join, paths) + return os.path.join(*paths) # convert_path () Modified: python/branches/release26-maint/Lib/idlelib/AutoCompleteWindow.py ============================================================================== --- python/branches/release26-maint/Lib/idlelib/AutoCompleteWindow.py (original) +++ python/branches/release26-maint/Lib/idlelib/AutoCompleteWindow.py Mon Aug 2 22:26:41 2010 @@ -349,10 +349,8 @@ self.lastkey_was_tab = True return - elif reduce(lambda x, y: x or y, - [keysym.find(s) != -1 for s in ("Shift", "Control", "Alt", - "Meta", "Command", "Option") - ]): + elif any(s in keysym for s in ("Shift", "Control", "Alt", + "Meta", "Command", "Option")): # A modifier key, so ignore return Modified: python/branches/release26-maint/Lib/idlelib/MultiCall.py ============================================================================== --- python/branches/release26-maint/Lib/idlelib/MultiCall.py (original) +++ python/branches/release26-maint/Lib/idlelib/MultiCall.py Mon Aug 2 22:26:41 2010 @@ -107,10 +107,9 @@ # a list of the states which are a subset of it. This list is ordered by the # number of modifiers is the state - the most specific state comes first. _states = range(1 << len(_modifiers)) -_state_names = [reduce(lambda x, y: x + y, - [_modifiers[i][0]+'-' for i in range(len(_modifiers)) - if (1 << i) & s], - "") +_state_names = [''.join(m[0]+'-' + for i, m in enumerate(_modifiers) + if (1 << i) & s) for s in _states] _state_subsets = map(lambda i: filter(lambda j: not (j & (~i)), _states), _states) @@ -119,11 +118,13 @@ range(len(_modifiers)))): nummod(b) - nummod(a)) # _state_codes gives for each state, the portable code to be passed as mc_state -_state_codes = [reduce(lambda x, y: x | y, - [_modifier_masks[i] for i in range(len(_modifiers)) - if (1 << i) & s], - 0) - for s in _states] +_state_codes = [] +for s in _states: + r = 0 + for i in range(len(_modifiers)): + if (1 << i) & s: + r |= _modifier_masks[i] + _state_codes.append(r) class _ComplexBinder: # This class binds many functions, and only unbinds them when it is deleted. Modified: python/branches/release26-maint/Lib/platform.py ============================================================================== --- python/branches/release26-maint/Lib/platform.py (original) +++ python/branches/release26-maint/Lib/platform.py Mon Aug 2 22:26:41 2010 @@ -1012,7 +1012,7 @@ if sys.platform in ('dos','win32','win16','os2'): # XXX Others too ? return default - target = _follow_symlinks(target) + target = _follow_symlinks(target).replace('"', '\\"') try: f = os.popen('file "%s" 2> /dev/null' % target) except (AttributeError,os.error): @@ -1078,13 +1078,13 @@ executable == sys.executable: # "file" command did not return anything; we'll try to provide # some sensible defaults then... - if _default_architecture.has_key(sys.platform): - b,l = _default_architecture[sys.platform] + if sys.platform in _default_architecture: + b, l = _default_architecture[sys.platform] if b: bits = b if l: linkage = l - return bits,linkage + return bits, linkage # Split the output into a list of strings omitting the filename fileout = _architecture_split(output)[1:] From python-checkins at python.org Mon Aug 2 22:27:20 2010 From: python-checkins at python.org (georg.brandl) Date: Mon, 2 Aug 2010 22:27:20 +0200 (CEST) Subject: [Python-checkins] r83565 - python/branches/py3k/Doc/library/cmd.rst Message-ID: <20100802202720.818ACEEAEA@mail.python.org> Author: georg.brandl Date: Mon Aug 2 22:27:20 2010 New Revision: 83565 Log: #9111: document that do_help() looks at docstrings. Modified: python/branches/py3k/Doc/library/cmd.rst Modified: python/branches/py3k/Doc/library/cmd.rst ============================================================================== --- python/branches/py3k/Doc/library/cmd.rst (original) +++ python/branches/py3k/Doc/library/cmd.rst Mon Aug 2 22:27:20 2010 @@ -76,11 +76,13 @@ are the beginning and ending indexes of the prefix text, which could be used to provide different completion depending upon which position the argument is in. - All subclasses of :class:`Cmd` inherit a predefined :meth:`do_help`. This + All subclasses of :class:`Cmd` inherit a predefined :meth:`do_help`. This method, called with an argument ``'bar'``, invokes the corresponding method - :meth:`help_bar`. With no argument, :meth:`do_help` lists all available help - topics (that is, all commands with corresponding :meth:`help_\*` methods), and - also lists any undocumented commands. + :meth:`help_bar`, and if that is not present, prints the docstring of + :meth:`do_bar`, if available. With no argument, :meth:`do_help` lists all + available help topics (that is, all commands with corresponding + :meth:`help_\*` methods or commands that have docstrings), and also lists any + undocumented commands. .. method:: Cmd.onecmd(str) From python-checkins at python.org Mon Aug 2 22:30:57 2010 From: python-checkins at python.org (georg.brandl) Date: Mon, 2 Aug 2010 22:30:57 +0200 (CEST) Subject: [Python-checkins] r83566 - python/branches/py3k/Doc/library/wsgiref.rst Message-ID: <20100802203057.CB4A7EEAF0@mail.python.org> Author: georg.brandl Date: Mon Aug 2 22:30:57 2010 New Revision: 83566 Log: #9019: remove false (in 3k) claim about Headers updates. Modified: python/branches/py3k/Doc/library/wsgiref.rst Modified: python/branches/py3k/Doc/library/wsgiref.rst ============================================================================== --- python/branches/py3k/Doc/library/wsgiref.rst (original) +++ python/branches/py3k/Doc/library/wsgiref.rst Mon Aug 2 22:30:57 2010 @@ -187,9 +187,7 @@ .. class:: Headers(headers) Create a mapping-like object wrapping *headers*, which must be a list of header - name/value tuples as described in :pep:`333`. Any changes made to the new - :class:`Headers` object will directly update the *headers* list it was created - with. + name/value tuples as described in :pep:`333`. :class:`Headers` objects support typical mapping operations including :meth:`__getitem__`, :meth:`get`, :meth:`__setitem__`, :meth:`setdefault`, From python-checkins at python.org Mon Aug 2 22:32:03 2010 From: python-checkins at python.org (georg.brandl) Date: Mon, 2 Aug 2010 22:32:03 +0200 (CEST) Subject: [Python-checkins] r83567 - in python/branches/release27-maint: Doc/library/constants.rst Message-ID: <20100802203203.8AEEFEEBB5@mail.python.org> Author: georg.brandl Date: Mon Aug 2 22:32:03 2010 New Revision: 83567 Log: Merged revisions 83552 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83552 | georg.brandl | 2010-08-02 21:36:36 +0200 (Mo, 02 Aug 2010) | 1 line #9438: clarify that constant names also cannot be assigned as attributes. ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Doc/library/constants.rst Modified: python/branches/release27-maint/Doc/library/constants.rst ============================================================================== --- python/branches/release27-maint/Doc/library/constants.rst (original) +++ python/branches/release27-maint/Doc/library/constants.rst Mon Aug 2 22:32:03 2010 @@ -3,7 +3,6 @@ A small number of constants live in the built-in namespace. They are: - .. data:: False The false value of the :class:`bool` type. @@ -39,16 +38,23 @@ Special value used in conjunction with extended slicing syntax. - .. XXX Someone who understands extended slicing should fill in here. - .. data:: __debug__ This constant is true if Python was not started with an :option:`-O` option. - Assignments to :const:`__debug__` are illegal and raise a :exc:`SyntaxError`. See also the :keyword:`assert` statement. +.. note:: + + The names :data:`None` and :data:`__debug__` cannot be reassigned + (assignments to them, even as an attribute name, raise :exc:`SyntaxError`), + so they can be considered "true" constants. + + .. versionchanged:: 2.7 + Assignments to ``__debug__`` as an attribute became illegal. + + Constants added by the :mod:`site` module ----------------------------------------- From python-checkins at python.org Mon Aug 2 22:32:20 2010 From: python-checkins at python.org (georg.brandl) Date: Mon, 2 Aug 2010 22:32:20 +0200 (CEST) Subject: [Python-checkins] r83568 - python/branches/release27-maint Message-ID: <20100802203220.7EAB3EEADF@mail.python.org> Author: georg.brandl Date: Mon Aug 2 22:32:20 2010 New Revision: 83568 Log: Blocked revisions 83566 via svnmerge ........ r83566 | georg.brandl | 2010-08-02 22:30:57 +0200 (Mo, 02 Aug 2010) | 1 line #9019: remove false (in 3k) claim about Headers updates. ........ Modified: python/branches/release27-maint/ (props changed) From python-checkins at python.org Mon Aug 2 22:39:36 2010 From: python-checkins at python.org (georg.brandl) Date: Mon, 2 Aug 2010 22:39:36 +0200 (CEST) Subject: [Python-checkins] r83569 - python/branches/py3k/Doc/library/base64.rst Message-ID: <20100802203936.2B870EEAEA@mail.python.org> Author: georg.brandl Date: Mon Aug 2 22:39:35 2010 New Revision: 83569 Log: #7797: be explicit about bytes-oriented interface of base64 functions. Modified: python/branches/py3k/Doc/library/base64.rst Modified: python/branches/py3k/Doc/library/base64.rst ============================================================================== --- python/branches/py3k/Doc/library/base64.rst (original) +++ python/branches/py3k/Doc/library/base64.rst Mon Aug 2 22:39:35 2010 @@ -24,7 +24,7 @@ .. function:: b64encode(s, altchars=None) - Encode a string use Base64. + Encode a byte string use Base64. *s* is the string to encode. Optional *altchars* must be a string of at least length 2 (additional characters are ignored) which specifies an alternative @@ -32,54 +32,54 @@ generate URL or filesystem safe Base64 strings. The default is ``None``, for which the standard Base64 alphabet is used. - The encoded string is returned. + The encoded byte string is returned. .. function:: b64decode(s, altchars=None) - Decode a Base64 encoded string. + Decode a Base64 encoded byte string. *s* is the string to decode. Optional *altchars* must be a string of at least length 2 (additional characters are ignored) which specifies the alternative alphabet used instead of the ``+`` and ``/`` characters. - The decoded string is returned. A :exc:`TypeError` is raised if *s* were + The decoded byte string is returned. A :exc:`TypeError` is raised if *s* were incorrectly padded or if there are non-alphabet characters present in the string. .. function:: standard_b64encode(s) - Encode string *s* using the standard Base64 alphabet. + Encode byte string *s* using the standard Base64 alphabet. .. function:: standard_b64decode(s) - Decode string *s* using the standard Base64 alphabet. + Decode byte string *s* using the standard Base64 alphabet. .. function:: urlsafe_b64encode(s) - Encode string *s* using a URL-safe alphabet, which substitutes ``-`` instead of + Encode byte string *s* using a URL-safe alphabet, which substitutes ``-`` instead of ``+`` and ``_`` instead of ``/`` in the standard Base64 alphabet. The result can still contain ``=``. .. function:: urlsafe_b64decode(s) - Decode string *s* using a URL-safe alphabet, which substitutes ``-`` instead of + Decode byte string *s* using a URL-safe alphabet, which substitutes ``-`` instead of ``+`` and ``_`` instead of ``/`` in the standard Base64 alphabet. .. function:: b32encode(s) - Encode a string using Base32. *s* is the string to encode. The encoded string + Encode a byte string using Base32. *s* is the string to encode. The encoded string is returned. .. function:: b32decode(s, casefold=False, map01=None) - Decode a Base32 encoded string. + Decode a Base32 encoded byte string. *s* is the string to decode. Optional *casefold* is a flag specifying whether a lowercase alphabet is acceptable as input. For security purposes, the default @@ -92,27 +92,27 @@ digit 0 is always mapped to the letter O). For security purposes the default is ``None``, so that 0 and 1 are not allowed in the input. - The decoded string is returned. A :exc:`TypeError` is raised if *s* were + The decoded byte string is returned. A :exc:`TypeError` is raised if *s* were incorrectly padded or if there are non-alphabet characters present in the string. .. function:: b16encode(s) - Encode a string using Base16. + Encode a byte string using Base16. - *s* is the string to encode. The encoded string is returned. + *s* is the string to encode. The encoded byte string is returned. .. function:: b16decode(s, casefold=False) - Decode a Base16 encoded string. + Decode a Base16 encoded byte string. *s* is the string to decode. Optional *casefold* is a flag specifying whether a lowercase alphabet is acceptable as input. For security purposes, the default is ``False``. - The decoded string is returned. A :exc:`TypeError` is raised if *s* were + The decoded byte string is returned. A :exc:`TypeError` is raised if *s* were incorrectly padded or if there are non-alphabet characters present in the string. From python-checkins at python.org Mon Aug 2 22:40:21 2010 From: python-checkins at python.org (ezio.melotti) Date: Mon, 2 Aug 2010 22:40:21 +0200 (CEST) Subject: [Python-checkins] r83570 - in python/branches/release26-maint: Lib/idlelib/AutoComplete.py Lib/idlelib/AutoCompleteWindow.py Lib/idlelib/Bindings.py Lib/idlelib/CallTips.py Lib/idlelib/ClassBrowser.py Lib/idlelib/CodeContext.py Lib/idlelib/ColorDelegator.py Lib/idlelib/Debugger.py Lib/idlelib/EditorWindow.py Lib/idlelib/FileList.py Lib/idlelib/FormatParagraph.py Lib/idlelib/GrepDialog.py Lib/idlelib/HyperParser.py Lib/idlelib/IOBinding.py Lib/idlelib/IdleHistory.py Lib/idlelib/MultiCall.py Lib/idlelib/ObjectBrowser.py Lib/idlelib/OutputWindow.py Lib/idlelib/ParenMatch.py Lib/idlelib/PathBrowser.py Lib/idlelib/Percolator.py Lib/idlelib/PyShell.py Lib/idlelib/RemoteDebugger.py Lib/idlelib/RemoteObjectBrowser.py Lib/idlelib/ReplaceDialog.py Lib/idlelib/ScriptBinding.py Lib/idlelib/SearchDialog.py Lib/idlelib/StackViewer.py Lib/idlelib/TreeWidget.py Lib/idlelib/UndoDelegator.py Lib/idlelib/ZoomHeight.py Lib/idlelib/aboutDialog.py Lib/idlelib/configDialog.py Lib/idlelib/configHandler.py Lib/idlelib/keybindingDialog.py Lib/idlelib/macosxSupport.py Lib/idlelib/run.py Message-ID: <20100802204021.12538EEB46@mail.python.org> Author: ezio.melotti Date: Mon Aug 2 22:40:20 2010 New Revision: 83570 Log: Merged revisions 79576-79578 via svnmerge from svn+ssh://pythondev at svn.python.org/python/trunk ........ r79576 | florent.xicluna | 2010-04-02 10:24:52 +0300 (Fri, 02 Apr 2010) | 2 lines #7092: Fix additional "-3" warnings in the idlelib package, and convert to absolute imports. ........ r79577 | florent.xicluna | 2010-04-02 11:15:26 +0300 (Fri, 02 Apr 2010) | 2 lines #7092: Drop the cmp argument. ........ r79578 | florent.xicluna | 2010-04-02 11:30:21 +0300 (Fri, 02 Apr 2010) | 2 lines #7092: silence some py3k warnings ........ Modified: python/branches/release26-maint/ (props changed) python/branches/release26-maint/Lib/idlelib/AutoComplete.py python/branches/release26-maint/Lib/idlelib/AutoCompleteWindow.py python/branches/release26-maint/Lib/idlelib/Bindings.py python/branches/release26-maint/Lib/idlelib/CallTips.py python/branches/release26-maint/Lib/idlelib/ClassBrowser.py python/branches/release26-maint/Lib/idlelib/CodeContext.py python/branches/release26-maint/Lib/idlelib/ColorDelegator.py python/branches/release26-maint/Lib/idlelib/Debugger.py python/branches/release26-maint/Lib/idlelib/EditorWindow.py python/branches/release26-maint/Lib/idlelib/FileList.py python/branches/release26-maint/Lib/idlelib/FormatParagraph.py python/branches/release26-maint/Lib/idlelib/GrepDialog.py python/branches/release26-maint/Lib/idlelib/HyperParser.py python/branches/release26-maint/Lib/idlelib/IOBinding.py python/branches/release26-maint/Lib/idlelib/IdleHistory.py python/branches/release26-maint/Lib/idlelib/MultiCall.py python/branches/release26-maint/Lib/idlelib/ObjectBrowser.py python/branches/release26-maint/Lib/idlelib/OutputWindow.py python/branches/release26-maint/Lib/idlelib/ParenMatch.py python/branches/release26-maint/Lib/idlelib/PathBrowser.py python/branches/release26-maint/Lib/idlelib/Percolator.py python/branches/release26-maint/Lib/idlelib/PyShell.py python/branches/release26-maint/Lib/idlelib/RemoteDebugger.py python/branches/release26-maint/Lib/idlelib/RemoteObjectBrowser.py python/branches/release26-maint/Lib/idlelib/ReplaceDialog.py python/branches/release26-maint/Lib/idlelib/ScriptBinding.py python/branches/release26-maint/Lib/idlelib/SearchDialog.py python/branches/release26-maint/Lib/idlelib/StackViewer.py python/branches/release26-maint/Lib/idlelib/TreeWidget.py python/branches/release26-maint/Lib/idlelib/UndoDelegator.py python/branches/release26-maint/Lib/idlelib/ZoomHeight.py python/branches/release26-maint/Lib/idlelib/aboutDialog.py python/branches/release26-maint/Lib/idlelib/configDialog.py python/branches/release26-maint/Lib/idlelib/configHandler.py python/branches/release26-maint/Lib/idlelib/keybindingDialog.py python/branches/release26-maint/Lib/idlelib/macosxSupport.py python/branches/release26-maint/Lib/idlelib/run.py Modified: python/branches/release26-maint/Lib/idlelib/AutoComplete.py ============================================================================== --- python/branches/release26-maint/Lib/idlelib/AutoComplete.py (original) +++ python/branches/release26-maint/Lib/idlelib/AutoComplete.py Mon Aug 2 22:40:20 2010 @@ -7,12 +7,7 @@ import sys import string -from configHandler import idleConf - -import AutoCompleteWindow -from HyperParser import HyperParser - -import __main__ +from idlelib.configHandler import idleConf # This string includes all chars that may be in a file name (without a path # separator) @@ -23,6 +18,11 @@ # These constants represent the two different types of completions COMPLETE_ATTRIBUTES, COMPLETE_FILES = range(1, 2+1) +from idlelib import AutoCompleteWindow +from idlelib.HyperParser import HyperParser + +import __main__ + SEPS = os.sep if os.altsep: # e.g. '/' on Windows... SEPS += os.altsep @@ -193,7 +193,7 @@ smalll = eval("__all__", namespace) smalll.sort() else: - smalll = filter(lambda s: s[:1] != '_', bigl) + smalll = [s for s in bigl if s[:1] != '_'] else: try: entity = self.get_entity(what) @@ -203,7 +203,7 @@ smalll = entity.__all__ smalll.sort() else: - smalll = filter(lambda s: s[:1] != '_', bigl) + smalll = [s for s in bigl if s[:1] != '_'] except: return [], [] @@ -214,7 +214,7 @@ expandedpath = os.path.expanduser(what) bigl = os.listdir(expandedpath) bigl.sort() - smalll = filter(lambda s: s[:1] != '.', bigl) + smalll = [s for s in bigl if s[:1] != '.'] except OSError: return [], [] Modified: python/branches/release26-maint/Lib/idlelib/AutoCompleteWindow.py ============================================================================== --- python/branches/release26-maint/Lib/idlelib/AutoCompleteWindow.py (original) +++ python/branches/release26-maint/Lib/idlelib/AutoCompleteWindow.py Mon Aug 2 22:40:20 2010 @@ -2,8 +2,8 @@ An auto-completion window for IDLE, used by the AutoComplete extension """ from Tkinter import * -from MultiCall import MC_SHIFT -import AutoComplete +from idlelib.MultiCall import MC_SHIFT +from idlelib.AutoComplete import COMPLETE_FILES, COMPLETE_ATTRIBUTES HIDE_VIRTUAL_EVENT_NAME = "<>" HIDE_SEQUENCES = ("", "") @@ -264,7 +264,7 @@ if keysym != "Tab": self.lastkey_was_tab = False if (len(keysym) == 1 or keysym in ("underscore", "BackSpace") - or (self.mode==AutoComplete.COMPLETE_FILES and keysym in + or (self.mode == COMPLETE_FILES and keysym in ("period", "minus"))) \ and not (state & ~MC_SHIFT): # Normal editing of text @@ -292,10 +292,10 @@ self.hide_window() return - elif (self.mode == AutoComplete.COMPLETE_ATTRIBUTES and keysym in + elif (self.mode == COMPLETE_ATTRIBUTES and keysym in ("period", "space", "parenleft", "parenright", "bracketleft", "bracketright")) or \ - (self.mode == AutoComplete.COMPLETE_FILES and keysym in + (self.mode == COMPLETE_FILES and keysym in ("slash", "backslash", "quotedbl", "apostrophe")) \ and not (state & ~MC_SHIFT): # If start is a prefix of the selection, but is not '' when @@ -303,7 +303,7 @@ # selected completion. Anyway, close the list. cursel = int(self.listbox.curselection()[0]) if self.completions[cursel][:len(self.start)] == self.start \ - and (self.mode==AutoComplete.COMPLETE_ATTRIBUTES or self.start): + and (self.mode == COMPLETE_ATTRIBUTES or self.start): self._change_start(self.completions[cursel]) self.hide_window() return Modified: python/branches/release26-maint/Lib/idlelib/Bindings.py ============================================================================== --- python/branches/release26-maint/Lib/idlelib/Bindings.py (original) +++ python/branches/release26-maint/Lib/idlelib/Bindings.py Mon Aug 2 22:40:20 2010 @@ -9,8 +9,8 @@ """ import sys -from configHandler import idleConf -import macosxSupport +from idlelib.configHandler import idleConf +from idlelib import macosxSupport menudefs = [ # underscore prefixes character to underscore Modified: python/branches/release26-maint/Lib/idlelib/CallTips.py ============================================================================== --- python/branches/release26-maint/Lib/idlelib/CallTips.py (original) +++ python/branches/release26-maint/Lib/idlelib/CallTips.py Mon Aug 2 22:40:20 2010 @@ -9,8 +9,8 @@ import sys import types -import CallTipWindow -from HyperParser import HyperParser +from idlelib import CallTipWindow +from idlelib.HyperParser import HyperParser import __main__ Modified: python/branches/release26-maint/Lib/idlelib/ClassBrowser.py ============================================================================== --- python/branches/release26-maint/Lib/idlelib/ClassBrowser.py (original) +++ python/branches/release26-maint/Lib/idlelib/ClassBrowser.py Mon Aug 2 22:40:20 2010 @@ -14,10 +14,10 @@ import sys import pyclbr -import PyShell -from WindowList import ListedToplevel -from TreeWidget import TreeNode, TreeItem, ScrolledCanvas -from configHandler import idleConf +from idlelib import PyShell +from idlelib.WindowList import ListedToplevel +from idlelib.TreeWidget import TreeNode, TreeItem, ScrolledCanvas +from idlelib.configHandler import idleConf class ClassBrowser: Modified: python/branches/release26-maint/Lib/idlelib/CodeContext.py ============================================================================== --- python/branches/release26-maint/Lib/idlelib/CodeContext.py (original) +++ python/branches/release26-maint/Lib/idlelib/CodeContext.py Mon Aug 2 22:40:20 2010 @@ -11,9 +11,9 @@ """ import Tkinter from Tkconstants import TOP, LEFT, X, W, SUNKEN -from configHandler import idleConf import re from sys import maxint as INFINITY +from idlelib.configHandler import idleConf BLOCKOPENERS = set(["class", "def", "elif", "else", "except", "finally", "for", "if", "try", "while", "with"]) Modified: python/branches/release26-maint/Lib/idlelib/ColorDelegator.py ============================================================================== --- python/branches/release26-maint/Lib/idlelib/ColorDelegator.py (original) +++ python/branches/release26-maint/Lib/idlelib/ColorDelegator.py Mon Aug 2 22:40:20 2010 @@ -3,8 +3,8 @@ import keyword import __builtin__ from Tkinter import * -from Delegator import Delegator -from configHandler import idleConf +from idlelib.Delegator import Delegator +from idlelib.configHandler import idleConf DEBUG = False @@ -248,7 +248,7 @@ self.tag_remove(tag, "1.0", "end") def main(): - from Percolator import Percolator + from idlelib.Percolator import Percolator root = Tk() root.wm_protocol("WM_DELETE_WINDOW", root.quit) text = Text(background="white") Modified: python/branches/release26-maint/Lib/idlelib/Debugger.py ============================================================================== --- python/branches/release26-maint/Lib/idlelib/Debugger.py (original) +++ python/branches/release26-maint/Lib/idlelib/Debugger.py Mon Aug 2 22:40:20 2010 @@ -2,9 +2,9 @@ import bdb import types from Tkinter import * -from WindowList import ListedToplevel -from ScrolledList import ScrolledList -import macosxSupport +from idlelib.WindowList import ListedToplevel +from idlelib.ScrolledList import ScrolledList +from idlelib import macosxSupport class Idb(bdb.Bdb): Modified: python/branches/release26-maint/Lib/idlelib/EditorWindow.py ============================================================================== --- python/branches/release26-maint/Lib/idlelib/EditorWindow.py (original) +++ python/branches/release26-maint/Lib/idlelib/EditorWindow.py Mon Aug 2 22:40:20 2010 @@ -6,18 +6,18 @@ from Tkinter import * import tkSimpleDialog import tkMessageBox -from MultiCall import MultiCallCreator - import webbrowser -import idlever -import WindowList -import SearchDialog -import GrepDialog -import ReplaceDialog -import PyParse -from configHandler import idleConf -import aboutDialog, textView, configDialog -import macosxSupport + +from idlelib.MultiCall import MultiCallCreator +from idlelib import idlever +from idlelib import WindowList +from idlelib import SearchDialog +from idlelib import GrepDialog +from idlelib import ReplaceDialog +from idlelib import PyParse +from idlelib.configHandler import idleConf +from idlelib import aboutDialog, textView, configDialog +from idlelib import macosxSupport # The default tab setting for a Text widget, in average-width characters. TK_TABWIDTH_DEFAULT = 8 @@ -50,13 +50,13 @@ return file, filename, descr class EditorWindow(object): - from Percolator import Percolator - from ColorDelegator import ColorDelegator - from UndoDelegator import UndoDelegator - from IOBinding import IOBinding, filesystemencoding, encoding - import Bindings + from idlelib.Percolator import Percolator + from idlelib.ColorDelegator import ColorDelegator + from idlelib.UndoDelegator import UndoDelegator + from idlelib.IOBinding import IOBinding, filesystemencoding, encoding + from idlelib import Bindings from Tkinter import Toplevel - from MultiStatusBar import MultiStatusBar + from idlelib.MultiStatusBar import MultiStatusBar help_url = None @@ -579,11 +579,11 @@ return None head, tail = os.path.split(filename) base, ext = os.path.splitext(tail) - import ClassBrowser + from idlelib import ClassBrowser ClassBrowser.ClassBrowser(self.flist, base, [head]) def open_path_browser(self, event=None): - import PathBrowser + from idlelib import PathBrowser PathBrowser.PathBrowser(self.flist) def gotoline(self, lineno): @@ -1193,7 +1193,7 @@ if not self.context_use_ps1: for context in self.num_context_lines: startat = max(lno - context, 1) - startatindex = `startat` + ".0" + startatindex = repr(startat) + ".0" rawtext = text.get(startatindex, "insert") y.set_str(rawtext) bod = y.find_good_parse_start( Modified: python/branches/release26-maint/Lib/idlelib/FileList.py ============================================================================== --- python/branches/release26-maint/Lib/idlelib/FileList.py (original) +++ python/branches/release26-maint/Lib/idlelib/FileList.py Mon Aug 2 22:40:20 2010 @@ -5,8 +5,8 @@ class FileList: - from EditorWindow import EditorWindow # class variable, may be overridden - # e.g. by PyShellFileList + # N.B. this import overridden in PyShellFileList. + from idlelib.EditorWindow import EditorWindow def __init__(self, root): self.root = root @@ -106,7 +106,7 @@ def _test(): - from EditorWindow import fixwordbreaks + from idlelib.EditorWindow import fixwordbreaks import sys root = Tk() fixwordbreaks(root) Modified: python/branches/release26-maint/Lib/idlelib/FormatParagraph.py ============================================================================== --- python/branches/release26-maint/Lib/idlelib/FormatParagraph.py (original) +++ python/branches/release26-maint/Lib/idlelib/FormatParagraph.py Mon Aug 2 22:40:20 2010 @@ -15,7 +15,7 @@ # * Fancy comments, like this bulleted list, arent handled :-) import re -from configHandler import idleConf +from idlelib.configHandler import idleConf class FormatParagraph: Modified: python/branches/release26-maint/Lib/idlelib/GrepDialog.py ============================================================================== --- python/branches/release26-maint/Lib/idlelib/GrepDialog.py (original) +++ python/branches/release26-maint/Lib/idlelib/GrepDialog.py Mon Aug 2 22:40:20 2010 @@ -2,8 +2,8 @@ import fnmatch import sys from Tkinter import * -import SearchEngine -from SearchDialogBase import SearchDialogBase +from idlelib import SearchEngine +from idlelib.SearchDialogBase import SearchDialogBase def grep(text, io=None, flist=None): root = text._root() @@ -63,7 +63,7 @@ if not path: self.top.bell() return - from OutputWindow import OutputWindow + from idlelib.OutputWindow import OutputWindow save = sys.stdout try: sys.stdout = OutputWindow(self.flist) Modified: python/branches/release26-maint/Lib/idlelib/HyperParser.py ============================================================================== --- python/branches/release26-maint/Lib/idlelib/HyperParser.py (original) +++ python/branches/release26-maint/Lib/idlelib/HyperParser.py Mon Aug 2 22:40:20 2010 @@ -10,7 +10,7 @@ import string import keyword -import PyParse +from idlelib import PyParse class HyperParser: @@ -31,7 +31,7 @@ if not editwin.context_use_ps1: for context in editwin.num_context_lines: startat = max(lno - context, 1) - startatindex = `startat` + ".0" + startatindex = repr(startat) + ".0" stopatindex = "%d.end" % lno # We add the newline because PyParse requires a newline at end. # We add a space so that index won't be at end of line, so that Modified: python/branches/release26-maint/Lib/idlelib/IOBinding.py ============================================================================== --- python/branches/release26-maint/Lib/idlelib/IOBinding.py (original) +++ python/branches/release26-maint/Lib/idlelib/IOBinding.py Mon Aug 2 22:40:20 2010 @@ -16,7 +16,7 @@ from Tkinter import * from SimpleDialog import SimpleDialog -from configHandler import idleConf +from idlelib.configHandler import idleConf try: from codecs import BOM_UTF8 Modified: python/branches/release26-maint/Lib/idlelib/IdleHistory.py ============================================================================== --- python/branches/release26-maint/Lib/idlelib/IdleHistory.py (original) +++ python/branches/release26-maint/Lib/idlelib/IdleHistory.py Mon Aug 2 22:40:20 2010 @@ -1,4 +1,4 @@ -from configHandler import idleConf +from idlelib.configHandler import idleConf class History: Modified: python/branches/release26-maint/Lib/idlelib/MultiCall.py ============================================================================== --- python/branches/release26-maint/Lib/idlelib/MultiCall.py (original) +++ python/branches/release26-maint/Lib/idlelib/MultiCall.py Mon Aug 2 22:40:20 2010 @@ -33,7 +33,7 @@ import string import re import Tkinter -import macosxSupport +from idlelib import macosxSupport # the event type constants, which define the meaning of mc_type MC_KEYPRESS=0; MC_KEYRELEASE=1; MC_BUTTONPRESS=2; MC_BUTTONRELEASE=3; @@ -111,12 +111,27 @@ for i, m in enumerate(_modifiers) if (1 << i) & s) for s in _states] -_state_subsets = map(lambda i: filter(lambda j: not (j & (~i)), _states), - _states) -for l in _state_subsets: - l.sort(lambda a, b, nummod = lambda x: len(filter(lambda i: (1<> #$ win @@ -336,7 +337,7 @@ return self.depth def main(): - from Percolator import Percolator + from idlelib.Percolator import Percolator root = Tk() root.wm_protocol("WM_DELETE_WINDOW", root.quit) text = Text() Modified: python/branches/release26-maint/Lib/idlelib/ZoomHeight.py ============================================================================== --- python/branches/release26-maint/Lib/idlelib/ZoomHeight.py (original) +++ python/branches/release26-maint/Lib/idlelib/ZoomHeight.py Mon Aug 2 22:40:20 2010 @@ -2,7 +2,8 @@ import re import sys -import macosxSupport + +from idlelib import macosxSupport class ZoomHeight: Modified: python/branches/release26-maint/Lib/idlelib/aboutDialog.py ============================================================================== --- python/branches/release26-maint/Lib/idlelib/aboutDialog.py (original) +++ python/branches/release26-maint/Lib/idlelib/aboutDialog.py Mon Aug 2 22:40:20 2010 @@ -4,9 +4,9 @@ from Tkinter import * import os -import os.path -import textView -import idlever + +from idlelib import textView +from idlelib import idlever class AboutDialog(Toplevel): """Modal about dialog for idle @@ -144,7 +144,7 @@ # test the dialog root = Tk() def run(): - import aboutDialog + from idlelib import aboutDialog aboutDialog.AboutDialog(root, 'About') Button(root, text='Dialog', command=run).pack() root.mainloop() Modified: python/branches/release26-maint/Lib/idlelib/configDialog.py ============================================================================== --- python/branches/release26-maint/Lib/idlelib/configDialog.py (original) +++ python/branches/release26-maint/Lib/idlelib/configDialog.py Mon Aug 2 22:40:20 2010 @@ -13,13 +13,13 @@ import tkMessageBox, tkColorChooser, tkFont import string -from configHandler import idleConf -from dynOptionMenuWidget import DynOptionMenu -from tabbedpages import TabbedPageSet -from keybindingDialog import GetKeysDialog -from configSectionNameDialog import GetCfgSectionNameDialog -from configHelpSourceEdit import GetHelpSourceDialog -import macosxSupport +from idlelib.configHandler import idleConf +from idlelib.dynOptionMenuWidget import DynOptionMenu +from idlelib.tabbedpages import TabbedPageSet +from idlelib.keybindingDialog import GetKeysDialog +from idlelib.configSectionNameDialog import GetCfgSectionNameDialog +from idlelib.configHelpSourceEdit import GetHelpSourceDialog +from idlelib import macosxSupport class ConfigDialog(Toplevel): @@ -988,16 +988,11 @@ self.SetThemeType() ##load theme element option menu themeNames=self.themeElements.keys() - themeNames.sort(self.__ThemeNameIndexCompare) + themeNames.sort(key=lambda x: self.themeElements[x][1]) self.optMenuHighlightTarget.SetMenu(themeNames,themeNames[0]) self.PaintThemeSample() self.SetHighlightTarget() - def __ThemeNameIndexCompare(self,a,b): - if self.themeElements[a][1] int(h2[2]): - return 1 - else: - return 0 - def GetAllExtraHelpSourcesList(self): """ Returns a list of tuples containing the details of all additional help Modified: python/branches/release26-maint/Lib/idlelib/keybindingDialog.py ============================================================================== --- python/branches/release26-maint/Lib/idlelib/keybindingDialog.py (original) +++ python/branches/release26-maint/Lib/idlelib/keybindingDialog.py Mon Aug 2 22:40:20 2010 @@ -132,7 +132,7 @@ order is also important: key binding equality depends on it, so config-keys.def must use the same ordering. """ - import macosxSupport + from idlelib import macosxSupport if macosxSupport.runningAsOSXApp(): self.modifiers = ['Shift', 'Control', 'Option', 'Command'] else: @@ -167,7 +167,7 @@ def GetModifiers(self): modList = [variable.get() for variable in self.modifier_vars] - return filter(None, modList) + return [mod for mod in modList if mod] def ClearKeySeq(self): self.listKeysFinal.select_clear(0,END) Modified: python/branches/release26-maint/Lib/idlelib/macosxSupport.py ============================================================================== --- python/branches/release26-maint/Lib/idlelib/macosxSupport.py (original) +++ python/branches/release26-maint/Lib/idlelib/macosxSupport.py Mon Aug 2 22:40:20 2010 @@ -51,10 +51,10 @@ # Due to a (mis-)feature of TkAqua the user will also see an empty Help # menu. from Tkinter import Menu, Text, Text - from EditorWindow import prepstr, get_accelerator - import Bindings - import WindowList - from MultiCall import MultiCallCreator + from idlelib.EditorWindow import prepstr, get_accelerator + from idlelib import Bindings + from idlelib import WindowList + from idlelib.MultiCall import MultiCallCreator menubar = Menu(root) root.configure(menu=menubar) @@ -77,11 +77,11 @@ menubar.add_cascade(label='IDLE', menu=menu) def about_dialog(event=None): - import aboutDialog + from idlelib import aboutDialog aboutDialog.AboutDialog(root, 'About IDLE') def config_dialog(event=None): - import configDialog + from idlelib import configDialog root.instance_dict = flist.inversedict configDialog.ConfigDialog(root, 'Settings') Modified: python/branches/release26-maint/Lib/idlelib/run.py ============================================================================== --- python/branches/release26-maint/Lib/idlelib/run.py (original) +++ python/branches/release26-maint/Lib/idlelib/run.py Mon Aug 2 22:40:20 2010 @@ -7,13 +7,13 @@ import threading import Queue -import CallTips -import AutoComplete +from idlelib import CallTips +from idlelib import AutoComplete -import RemoteDebugger -import RemoteObjectBrowser -import StackViewer -import rpc +from idlelib import RemoteDebugger +from idlelib import RemoteObjectBrowser +from idlelib import StackViewer +from idlelib import rpc import __main__ @@ -118,7 +118,7 @@ break except socket.error, err: print>>sys.__stderr__,"IDLE Subprocess: socket error: "\ - + err[1] + ", retrying...." + + err.args[1] + ", retrying...." else: print>>sys.__stderr__, "IDLE Subprocess: Connection to "\ "IDLE GUI failed, exiting." @@ -133,14 +133,15 @@ import tkMessageBox root = Tkinter.Tk() root.withdraw() - if err[0] == 61: # connection refused + if err.args[0] == 61: # connection refused msg = "IDLE's subprocess can't connect to %s:%d. This may be due "\ "to your personal firewall configuration. It is safe to "\ "allow this internal connection because no data is visible on "\ "external ports." % address tkMessageBox.showerror("IDLE Subprocess Error", msg, parent=root) else: - tkMessageBox.showerror("IDLE Subprocess Error", "Socket Error: %s" % err[1]) + tkMessageBox.showerror("IDLE Subprocess Error", + "Socket Error: %s" % err.args[1]) root.destroy() def print_exception(): @@ -253,7 +254,7 @@ sys.stdin = self.console = self.get_remote_proxy("stdin") sys.stdout = self.get_remote_proxy("stdout") sys.stderr = self.get_remote_proxy("stderr") - import IOBinding + from idlelib import IOBinding sys.stdin.encoding = sys.stdout.encoding = \ sys.stderr.encoding = IOBinding.encoding self.interp = self.get_remote_proxy("interp") From python-checkins at python.org Mon Aug 2 22:44:34 2010 From: python-checkins at python.org (georg.brandl) Date: Mon, 2 Aug 2010 22:44:34 +0200 (CEST) Subject: [Python-checkins] r83571 - python/branches/py3k/Doc/tutorial/classes.rst Message-ID: <20100802204434.B6D6EEE9B2@mail.python.org> Author: georg.brandl Date: Mon Aug 2 22:44:34 2010 New Revision: 83571 Log: Clarify that abs() is not a namespace. Modified: python/branches/py3k/Doc/tutorial/classes.rst Modified: python/branches/py3k/Doc/tutorial/classes.rst ============================================================================== --- python/branches/py3k/Doc/tutorial/classes.rst (original) +++ python/branches/py3k/Doc/tutorial/classes.rst Mon Aug 2 22:44:34 2010 @@ -65,7 +65,7 @@ A *namespace* is a mapping from names to objects. Most namespaces are currently implemented as Python dictionaries, but that's normally not noticeable in any way (except for performance), and it may change in the future. Examples of -namespaces are: the set of built-in names (functions such as :func:`abs`, and +namespaces are: the set of built-in names (containing functions such as :func:`abs`, and built-in exception names); the global names in a module; and the local names in a function invocation. In a sense the set of attributes of an object also form a namespace. The important thing to know about namespaces is that there is From python-checkins at python.org Mon Aug 2 22:46:25 2010 From: python-checkins at python.org (ezio.melotti) Date: Mon, 2 Aug 2010 22:46:25 +0200 (CEST) Subject: [Python-checkins] r83572 - python/branches/release26-maint Message-ID: <20100802204625.AE8B6EEA5B@mail.python.org> Author: ezio.melotti Date: Mon Aug 2 22:46:25 2010 New Revision: 83572 Log: Unblocked revisions 75408 via svnmerge ........ r75408 | antoine.pitrou | 2009-10-14 20:34:31 +0300 (Wed, 14 Oct 2009) | 3 lines Fix a test_atexit failure when run with -3 ........ Modified: python/branches/release26-maint/ (props changed) From python-checkins at python.org Mon Aug 2 22:47:06 2010 From: python-checkins at python.org (ezio.melotti) Date: Mon, 2 Aug 2010 22:47:06 +0200 (CEST) Subject: [Python-checkins] r83573 - in python/branches/release26-maint: Lib/test/test_atexit.py Message-ID: <20100802204706.64B53EEB2B@mail.python.org> Author: ezio.melotti Date: Mon Aug 2 22:47:06 2010 New Revision: 83573 Log: Merged revisions 75408 via svnmerge from svn+ssh://pythondev at svn.python.org/python/trunk ........ r75408 | antoine.pitrou | 2009-10-14 20:34:31 +0300 (Wed, 14 Oct 2009) | 3 lines Fix a test_atexit failure when run with -3 ........ Modified: python/branches/release26-maint/ (props changed) python/branches/release26-maint/Lib/test/test_atexit.py Modified: python/branches/release26-maint/Lib/test/test_atexit.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_atexit.py (original) +++ python/branches/release26-maint/Lib/test/test_atexit.py Mon Aug 2 22:47:06 2010 @@ -2,6 +2,7 @@ import unittest import StringIO import atexit +from imp import reload from test import test_support class TestCase(unittest.TestCase): From python-checkins at python.org Mon Aug 2 22:47:56 2010 From: python-checkins at python.org (georg.brandl) Date: Mon, 2 Aug 2010 22:47:56 +0200 (CEST) Subject: [Python-checkins] r83574 - python/branches/py3k/Modules/selectmodule.c Message-ID: <20100802204756.C5044F868@mail.python.org> Author: georg.brandl Date: Mon Aug 2 22:47:56 2010 New Revision: 83574 Log: #6867: epoll.register() returns None. Modified: python/branches/py3k/Modules/selectmodule.c Modified: python/branches/py3k/Modules/selectmodule.c ============================================================================== --- python/branches/py3k/Modules/selectmodule.c (original) +++ python/branches/py3k/Modules/selectmodule.c Mon Aug 2 22:47:56 2010 @@ -915,10 +915,9 @@ } PyDoc_STRVAR(pyepoll_register_doc, -"register(fd[, eventmask]) -> bool\n\ +"register(fd[, eventmask]) -> None\n\ \n\ -Registers a new fd or modifies an already registered fd. register() returns\n\ -True if a new fd was registered or False if the event mask for fd was modified.\n\ +Registers a new fd or modifies an already registered fd.\n\ fd is the target file descriptor of the operation.\n\ events is a bit set composed of the various EPOLL constants; the default\n\ is EPOLL_IN | EPOLL_OUT | EPOLL_PRI.\n\ From python-checkins at python.org Mon Aug 2 22:52:10 2010 From: python-checkins at python.org (georg.brandl) Date: Mon, 2 Aug 2010 22:52:10 +0200 (CEST) Subject: [Python-checkins] r83575 - python/branches/py3k/Doc/library/zipfile.rst Message-ID: <20100802205210.583EDEEA92@mail.python.org> Author: georg.brandl Date: Mon Aug 2 22:52:10 2010 New Revision: 83575 Log: #9238: zipfile does handle archive comments. Modified: python/branches/py3k/Doc/library/zipfile.rst Modified: python/branches/py3k/Doc/library/zipfile.rst ============================================================================== --- python/branches/py3k/Doc/library/zipfile.rst (original) +++ python/branches/py3k/Doc/library/zipfile.rst Mon Aug 2 22:52:10 2010 @@ -12,10 +12,8 @@ defined in `PKZIP Application Note `_. -This module does not currently handle multi-disk ZIP files, or ZIP files -which have appended comments (although it correctly handles comments -added to individual archive members---for which see the :ref:`zipinfo-objects` -documentation). It can handle ZIP files that use the ZIP64 extensions +This module does not currently handle multi-disk ZIP files. +It can handle ZIP files that use the ZIP64 extensions (that is ZIP files that are more than 4 GByte in size). It supports decryption of encrypted files in ZIP archives, but it currently cannot create an encrypted file. Decryption is extremely slow as it is @@ -64,7 +62,6 @@ Returns ``True`` if *filename* is a valid ZIP file based on its magic number, otherwise returns ``False``. *filename* may be a file or file-like object too. - This module does not currently handle ZIP files which have appended comments. .. versionchanged:: 3.1 Support for file and file-like objects. From python-checkins at python.org Mon Aug 2 22:52:47 2010 From: python-checkins at python.org (ezio.melotti) Date: Mon, 2 Aug 2010 22:52:47 +0200 (CEST) Subject: [Python-checkins] r83576 - python/branches/release26-maint/Lib/test/test_bool.py Message-ID: <20100802205247.E234AEEBE8@mail.python.org> Author: ezio.melotti Date: Mon Aug 2 22:52:47 2010 New Revision: 83576 Log: Fix test_bool. operator.isCallable() doesn't raise a warning on 2.6. Modified: python/branches/release26-maint/Lib/test/test_bool.py Modified: python/branches/release26-maint/Lib/test/test_bool.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_bool.py (original) +++ python/branches/release26-maint/Lib/test/test_bool.py Mon Aug 2 22:52:47 2010 @@ -261,9 +261,8 @@ import operator self.assertIs(operator.truth(0), False) self.assertIs(operator.truth(1), True) - with test_support._check_py3k_warnings(): - self.assertIs(operator.isCallable(0), False) - self.assertIs(operator.isCallable(len), True) + self.assertIs(operator.isCallable(0), False) + self.assertIs(operator.isCallable(len), True) self.assertIs(operator.isNumberType(None), False) self.assertIs(operator.isNumberType(0), True) self.assertIs(operator.not_(1), False) From python-checkins at python.org Mon Aug 2 22:54:56 2010 From: python-checkins at python.org (ezio.melotti) Date: Mon, 2 Aug 2010 22:54:56 +0200 (CEST) Subject: [Python-checkins] r83577 - python/branches/release26-maint/Lib/test/test_descr.py Message-ID: <20100802205456.C70FDEEB2B@mail.python.org> Author: ezio.melotti Date: Mon Aug 2 22:54:56 2010 New Revision: 83577 Log: Fix test_descr. Modified: python/branches/release26-maint/Lib/test/test_descr.py Modified: python/branches/release26-maint/Lib/test/test_descr.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_descr.py (original) +++ python/branches/release26-maint/Lib/test/test_descr.py Mon Aug 2 22:54:56 2010 @@ -4436,7 +4436,6 @@ with test_support._check_py3k_warnings( ("classic (int|long) division", DeprecationWarning), ("coerce.. not supported", DeprecationWarning), - ("Overriding __cmp__ ", DeprecationWarning), (".+__(get|set|del)slice__ has been removed", DeprecationWarning)): # Run all local test cases, with PTypesLongInitTest first. test_support.run_unittest(PTypesLongInitTest, OperatorsTest, From python-checkins at python.org Mon Aug 2 22:58:02 2010 From: python-checkins at python.org (ezio.melotti) Date: Mon, 2 Aug 2010 22:58:02 +0200 (CEST) Subject: [Python-checkins] r83578 - python/branches/release26-maint/Lib/test/test_dict.py Message-ID: <20100802205802.2FDFFEEBFE@mail.python.org> Author: ezio.melotti Date: Mon Aug 2 22:58:02 2010 New Revision: 83578 Log: Fix test_dict. Modified: python/branches/release26-maint/Lib/test/test_dict.py Modified: python/branches/release26-maint/Lib/test/test_dict.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_dict.py (original) +++ python/branches/release26-maint/Lib/test/test_dict.py Mon Aug 2 22:58:02 2010 @@ -549,7 +549,7 @@ # Bug #3537: if an empty but presized dict with a size larger # than 7 was in the freelist, it triggered an assertion failure try: - d = {'a': 1/0, 'b': None, 'c': None, 'd': None, 'e': None, + d = {'a': 1//0, 'b': None, 'c': None, 'd': None, 'e': None, 'f': None, 'g': None, 'h': None} except ZeroDivisionError: pass From python-checkins at python.org Mon Aug 2 23:00:33 2010 From: python-checkins at python.org (ezio.melotti) Date: Mon, 2 Aug 2010 23:00:33 +0200 (CEST) Subject: [Python-checkins] r83579 - python/branches/release26-maint/Lib/test/test_operator.py Message-ID: <20100802210033.21E77EEC07@mail.python.org> Author: ezio.melotti Date: Mon Aug 2 23:00:32 2010 New Revision: 83579 Log: Fix test_operator. Modified: python/branches/release26-maint/Lib/test/test_operator.py Modified: python/branches/release26-maint/Lib/test/test_operator.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_operator.py (original) +++ python/branches/release26-maint/Lib/test/test_operator.py Mon Aug 2 23:00:32 2010 @@ -192,8 +192,8 @@ class C: pass def check(self, o, v): + self.assertEqual(operator.isCallable(o), v) with test_support._check_py3k_warnings(): - self.assertEqual(operator.isCallable(o), v) self.assertEqual(callable(o), v) check(self, 4, 0) check(self, operator.isCallable, 1) @@ -308,9 +308,8 @@ self.assertRaises(TypeError, operator.contains, None, None) self.assertTrue(operator.contains(range(4), 2)) self.assertFalse(operator.contains(range(4), 5)) - with test_support._check_py3k_warnings(): - self.assertTrue(operator.sequenceIncludes(range(4), 2)) - self.assertFalse(operator.sequenceIncludes(range(4), 5)) + self.assertTrue(operator.sequenceIncludes(range(4), 2)) + self.assertFalse(operator.sequenceIncludes(range(4), 5)) def test_setitem(self): a = range(3) From python-checkins at python.org Mon Aug 2 23:02:36 2010 From: python-checkins at python.org (georg.brandl) Date: Mon, 2 Aug 2010 23:02:36 +0200 (CEST) Subject: [Python-checkins] r83580 - python/branches/py3k/configure.in Message-ID: <20100802210236.BD288EEBF3@mail.python.org> Author: georg.brandl Date: Mon Aug 2 23:02:36 2010 New Revision: 83580 Log: #8119: fix copy-paste error. Modified: python/branches/py3k/configure.in Modified: python/branches/py3k/configure.in ============================================================================== --- python/branches/py3k/configure.in (original) +++ python/branches/py3k/configure.in Mon Aug 2 23:02:36 2010 @@ -2458,7 +2458,7 @@ fi AC_MSG_RESULT($with_doc_strings) -# Check for Python-specific malloc support +# Check if eval loop should use timestamp counter profiling AC_MSG_CHECKING(for --with-tsc) AC_ARG_WITH(tsc, AS_HELP_STRING([--with(out)-tsc],[enable/disable timestamp counter profile]),[ From python-checkins at python.org Mon Aug 2 23:04:52 2010 From: python-checkins at python.org (ezio.melotti) Date: Mon, 2 Aug 2010 23:04:52 +0200 (CEST) Subject: [Python-checkins] r83581 - python/branches/release26-maint Message-ID: <20100802210452.C0FB6EEBE0@mail.python.org> Author: ezio.melotti Date: Mon Aug 2 23:04:52 2010 New Revision: 83581 Log: Unblocked revisions 75417 via svnmerge ........ r75417 | antoine.pitrou | 2009-10-14 21:47:13 +0300 (Wed, 14 Oct 2009) | 3 lines Fix failures in test_profilehooks when run with -3 ........ Modified: python/branches/release26-maint/ (props changed) From python-checkins at python.org Mon Aug 2 23:05:39 2010 From: python-checkins at python.org (ezio.melotti) Date: Mon, 2 Aug 2010 23:05:39 +0200 (CEST) Subject: [Python-checkins] r83582 - in python/branches/release26-maint: Lib/test/test_profilehooks.py Message-ID: <20100802210539.BC44DEEBDE@mail.python.org> Author: ezio.melotti Date: Mon Aug 2 23:05:39 2010 New Revision: 83582 Log: Merged revisions 75417 via svnmerge from svn+ssh://pythondev at svn.python.org/python/trunk ........ r75417 | antoine.pitrou | 2009-10-14 21:47:13 +0300 (Wed, 14 Oct 2009) | 3 lines Fix failures in test_profilehooks when run with -3 ........ Modified: python/branches/release26-maint/ (props changed) python/branches/release26-maint/Lib/test/test_profilehooks.py Modified: python/branches/release26-maint/Lib/test/test_profilehooks.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_profilehooks.py (original) +++ python/branches/release26-maint/Lib/test/test_profilehooks.py Mon Aug 2 23:05:39 2010 @@ -110,7 +110,7 @@ def test_exception(self): def f(p): - 1/0 + 1./0 f_ident = ident(f) self.check_events(f, [(1, 'call', f_ident), (1, 'return', f_ident), @@ -118,7 +118,7 @@ def test_caught_exception(self): def f(p): - try: 1/0 + try: 1./0 except: pass f_ident = ident(f) self.check_events(f, [(1, 'call', f_ident), @@ -127,7 +127,7 @@ def test_caught_nested_exception(self): def f(p): - try: 1/0 + try: 1./0 except: pass f_ident = ident(f) self.check_events(f, [(1, 'call', f_ident), @@ -136,7 +136,7 @@ def test_nested_exception(self): def f(p): - 1/0 + 1./0 f_ident = ident(f) self.check_events(f, [(1, 'call', f_ident), # This isn't what I expected: @@ -147,7 +147,7 @@ def test_exception_in_except_clause(self): def f(p): - 1/0 + 1./0 def g(p): try: f(p) @@ -166,7 +166,7 @@ def test_exception_propogation(self): def f(p): - 1/0 + 1./0 def g(p): try: f(p) finally: p.add_event("falling through") @@ -181,8 +181,8 @@ def test_raise_twice(self): def f(p): - try: 1/0 - except: 1/0 + try: 1./0 + except: 1./0 f_ident = ident(f) self.check_events(f, [(1, 'call', f_ident), (1, 'return', f_ident), @@ -190,7 +190,7 @@ def test_raise_reraise(self): def f(p): - try: 1/0 + try: 1./0 except: raise f_ident = ident(f) self.check_events(f, [(1, 'call', f_ident), @@ -207,7 +207,7 @@ def test_distant_exception(self): def f(): - 1/0 + 1./0 def g(): f() def h(): @@ -292,7 +292,7 @@ def test_basic_exception(self): def f(p): - 1/0 + 1./0 f_ident = ident(f) self.check_events(f, [(1, 'call', f_ident), (1, 'return', f_ident), @@ -300,7 +300,7 @@ def test_caught_exception(self): def f(p): - try: 1/0 + try: 1./0 except: pass f_ident = ident(f) self.check_events(f, [(1, 'call', f_ident), @@ -309,7 +309,7 @@ def test_distant_exception(self): def f(): - 1/0 + 1./0 def g(): f() def h(): From python-checkins at python.org Mon Aug 2 23:06:44 2010 From: python-checkins at python.org (ezio.melotti) Date: Mon, 2 Aug 2010 23:06:44 +0200 (CEST) Subject: [Python-checkins] r83583 - python/branches/release26-maint Message-ID: <20100802210644.6BFA2EEBF9@mail.python.org> Author: ezio.melotti Date: Mon Aug 2 23:06:44 2010 New Revision: 83583 Log: Unblocked revisions 75415 via svnmerge ........ r75415 | antoine.pitrou | 2009-10-14 21:39:46 +0300 (Wed, 14 Oct 2009) | 3 lines Silence some py3k warnings claiming to affect _pyio ........ Modified: python/branches/release26-maint/ (props changed) From python-checkins at python.org Mon Aug 2 23:07:15 2010 From: python-checkins at python.org (georg.brandl) Date: Mon, 2 Aug 2010 23:07:15 +0200 (CEST) Subject: [Python-checkins] r83584 - python/branches/py3k/README Message-ID: <20100802210715.1913AEEBFE@mail.python.org> Author: georg.brandl Date: Mon Aug 2 23:07:14 2010 New Revision: 83584 Log: #9457: fix documentation links for 3.2. Modified: python/branches/py3k/README Modified: python/branches/py3k/README ============================================================================== --- python/branches/py3k/README (original) +++ python/branches/py3k/README Mon Aug 2 23:07:14 2010 @@ -53,7 +53,7 @@ We try to have a comprehensive overview of the changes in the "What's New in Python 3.2" document, found at - http://docs.python.org/dev/3.2/whatsnew/3.2.html + http://docs.python.org/3.2/whatsnew/3.2.html For a more detailed change log, read Misc/NEWS (though this file, too, is incomplete, and also doesn't list anything merged in from the 2.7 release under @@ -68,7 +68,7 @@ Documentation for Python 3.2 is online, updated daily: - http://docs.python.org/dev/ + http://docs.python.org/3.2/ It can also be downloaded in many formats for faster access. The documentation is downloadable in HTML, PDF, and reStructuredText formats; the latter version @@ -86,7 +86,7 @@ A source-to-source translation tool, "2to3", can take care of the mundane task of converting large amounts of source code. It is not a complete solution but is complemented by the deprecation warnings in 2.6. See -http://docs.python.org/dev/library/2to3.html for more information. +http://docs.python.org/3.2/library/2to3.html for more information. Testing From python-checkins at python.org Mon Aug 2 23:23:16 2010 From: python-checkins at python.org (ezio.melotti) Date: Mon, 2 Aug 2010 23:23:16 +0200 (CEST) Subject: [Python-checkins] r83585 - python/branches/release26-maint Message-ID: <20100802212316.6EFF0EEBEC@mail.python.org> Author: ezio.melotti Date: Mon Aug 2 23:23:16 2010 New Revision: 83585 Log: Blocked revisions 75415 via svnmerge ........ r75415 | antoine.pitrou | 2009-10-14 21:39:46 +0300 (Wed, 14 Oct 2009) | 3 lines Silence some py3k warnings claiming to affect _pyio ........ Modified: python/branches/release26-maint/ (props changed) From python-checkins at python.org Mon Aug 2 23:23:45 2010 From: python-checkins at python.org (ezio.melotti) Date: Mon, 2 Aug 2010 23:23:45 +0200 (CEST) Subject: [Python-checkins] r83586 - python/branches/release26-maint Message-ID: <20100802212345.82D86EEA1E@mail.python.org> Author: ezio.melotti Date: Mon Aug 2 23:23:45 2010 New Revision: 83586 Log: Unblocked revisions 78757 via svnmerge ........ r78757 | florent.xicluna | 2010-03-07 14:14:25 +0200 (Sun, 07 Mar 2010) | 2 lines Fix some py3k warnings in the standard library. ........ Modified: python/branches/release26-maint/ (props changed) From python-checkins at python.org Mon Aug 2 23:29:14 2010 From: python-checkins at python.org (georg.brandl) Date: Mon, 2 Aug 2010 23:29:14 +0200 (CEST) Subject: [Python-checkins] r83587 - in python/branches/release27-maint: Lib/pstats.py Misc/NEWS Message-ID: <20100802212914.8D764EE9F2@mail.python.org> Author: georg.brandl Date: Mon Aug 2 23:29:14 2010 New Revision: 83587 Log: Merged revisions 83523 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83523 | georg.brandl | 2010-08-02 14:06:18 +0200 (Mo, 02 Aug 2010) | 1 line #9209 and #7781: fix two crashes in pstats interactive browser. ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Lib/pstats.py python/branches/release27-maint/Misc/NEWS Modified: python/branches/release27-maint/Lib/pstats.py ============================================================================== --- python/branches/release27-maint/Lib/pstats.py (original) +++ python/branches/release27-maint/Lib/pstats.py Mon Aug 2 23:29:14 2010 @@ -5,7 +5,7 @@ # Based on prior profile module by Sjoerd Mullender... # which was hacked somewhat by: Guido van Rossum # -# see profile.doc and profile.py for more info. +# see profile.py for more info. # Copyright 1994, by InfoSeek Corporation, all rights reserved. # Written by James Roskind @@ -66,7 +66,7 @@ minor key of 'the name of the function'. Look at the two tables in sort_stats() and get_sort_arg_defs(self) for more examples. - All methods return self, so you can string together commands like: + All methods return self, so you can string together commands like: Stats('foo', 'goo').strip_dirs().sort_stats('calls').\ print_stats(5).print_callers(5) """ @@ -150,7 +150,7 @@ if not arg_list: return self if len(arg_list) > 1: self.add(*arg_list[1:]) other = arg_list[0] - if type(self) != type(other) or self.__class__ != other.__class__: + if type(self) != type(other): other = Stats(other) self.files += other.files self.total_calls += other.total_calls @@ -218,12 +218,12 @@ if not field: self.fcn_list = 0 return self - if len(field) == 1 and type(field[0]) == type(1): + if len(field) == 1 and isinstance(field[0], int): # Be compatible with old profiler field = [ {-1: "stdname", - 0:"calls", - 1:"time", - 2: "cumulative" } [ field[0] ] ] + 0: "calls", + 1: "time", + 2: "cumulative"}[field[0]] ] sort_arg_defs = self.get_sort_arg_defs() sort_tuple = () @@ -300,48 +300,53 @@ def eval_print_amount(self, sel, list, msg): new_list = list - if type(sel) == type(""): + if isinstance(sel, str): + try: + rex = re.compile(sel) + except re.error: + msg += " \n" % sel + return new_list, msg new_list = [] for func in list: - if re.search(sel, func_std_string(func)): + if rex.search(func_std_string(func)): new_list.append(func) else: count = len(list) - if type(sel) == type(1.0) and 0.0 <= sel < 1.0: + if isinstance(sel, float) and 0.0 <= sel < 1.0: count = int(count * sel + .5) new_list = list[:count] - elif type(sel) == type(1) and 0 <= sel < count: + elif isinstance(sel, int) and 0 <= sel < count: count = sel new_list = list[:count] if len(list) != len(new_list): - msg = msg + " List reduced from %r to %r due to restriction <%r>\n" % ( - len(list), len(new_list), sel) + msg += " List reduced from %r to %r due to restriction <%r>\n" % ( + len(list), len(new_list), sel) return new_list, msg def get_print_list(self, sel_list): width = self.max_name_len if self.fcn_list: - list = self.fcn_list[:] + stat_list = self.fcn_list[:] msg = " Ordered by: " + self.sort_type + '\n' else: - list = self.stats.keys() + stat_list = list(self.stats.keys()) msg = " Random listing order was used\n" for selection in sel_list: - list, msg = self.eval_print_amount(selection, list, msg) + stat_list, msg = self.eval_print_amount(selection, stat_list, msg) - count = len(list) + count = len(stat_list) - if not list: - return 0, list + if not stat_list: + return 0, stat_list print >> self.stream, msg if count < len(self.stats): width = 0 - for func in list: + for func in stat_list: if len(func_std_string(func)) > width: width = len(func_std_string(func)) - return width+2, list + return width+2, stat_list def print_stats(self, *amount): for filename in self.files: @@ -553,12 +558,10 @@ def __init__(self, profile=None): cmd.Cmd.__init__(self) self.prompt = "% " + self.stats = None + self.stream = sys.stdout if profile is not None: - self.stats = Stats(profile) - self.stream = self.stats.stream - else: - self.stats = None - self.stream = sys.stdout + self.do_read(profile) def generic(self, fn, line): args = line.split() Modified: python/branches/release27-maint/Misc/NEWS ============================================================================== --- python/branches/release27-maint/Misc/NEWS (original) +++ python/branches/release27-maint/Misc/NEWS Mon Aug 2 23:29:14 2010 @@ -26,6 +26,12 @@ - Issue #9354: Provide getsockopt() in asyncore's file_wrapper. +- Issue #7781: Fix restricting stats by entry counts in the pstats + interactive browser. + +- Issue #9209: Do not crash in the pstats interactive browser on invalid + regular expressions. + - Issue #7372: Fix pstats regression when stripping paths from profile data generated with the profile module. From python-checkins at python.org Mon Aug 2 23:35:07 2010 From: python-checkins at python.org (ezio.melotti) Date: Mon, 2 Aug 2010 23:35:07 +0200 (CEST) Subject: [Python-checkins] r83588 - in python/branches/release26-maint: Lib/calendar.py Lib/ctypes/test/test_callbacks.py Lib/distutils/util.py Lib/io.py Lib/warnings.py Message-ID: <20100802213507.0C43DEEC13@mail.python.org> Author: ezio.melotti Date: Mon Aug 2 23:35:06 2010 New Revision: 83588 Log: Merged revisions 78757 via svnmerge from svn+ssh://pythondev at svn.python.org/python/trunk ........ r78757 | florent.xicluna | 2010-03-07 14:14:25 +0200 (Sun, 07 Mar 2010) | 2 lines Fix some py3k warnings in the standard library. ........ Modified: python/branches/release26-maint/ (props changed) python/branches/release26-maint/Lib/calendar.py python/branches/release26-maint/Lib/ctypes/test/test_callbacks.py python/branches/release26-maint/Lib/distutils/util.py python/branches/release26-maint/Lib/io.py python/branches/release26-maint/Lib/warnings.py Modified: python/branches/release26-maint/Lib/calendar.py ============================================================================== --- python/branches/release26-maint/Lib/calendar.py (original) +++ python/branches/release26-maint/Lib/calendar.py Mon Aug 2 23:35:06 2010 @@ -564,6 +564,10 @@ firstweekday = c.getfirstweekday def setfirstweekday(firstweekday): + try: + firstweekday.__index__ + except AttributeError: + raise IllegalWeekdayError(firstweekday) if not MONDAY <= firstweekday <= SUNDAY: raise IllegalWeekdayError(firstweekday) c.firstweekday = firstweekday Modified: python/branches/release26-maint/Lib/ctypes/test/test_callbacks.py ============================================================================== --- python/branches/release26-maint/Lib/ctypes/test/test_callbacks.py (original) +++ python/branches/release26-maint/Lib/ctypes/test/test_callbacks.py Mon Aug 2 23:35:06 2010 @@ -132,7 +132,7 @@ gc.collect() live = [x for x in gc.get_objects() if isinstance(x, X)] - self.failUnlessEqual(len(live), 0) + self.assertEqual(len(live), 0) try: WINFUNCTYPE @@ -164,7 +164,7 @@ result = integrate(0.0, 1.0, CALLBACK(func), 10) diff = abs(result - 1./3.) - self.failUnless(diff < 0.01, "%s not less than 0.01" % diff) + self.assertTrue(diff < 0.01, "%s not less than 0.01" % diff) ################################################################ Modified: python/branches/release26-maint/Lib/distutils/util.py ============================================================================== --- python/branches/release26-maint/Lib/distutils/util.py (original) +++ python/branches/release26-maint/Lib/distutils/util.py Mon Aug 2 23:35:06 2010 @@ -405,7 +405,7 @@ log.info(msg) if not dry_run: - apply(func, args) + func(*args) def strtobool (val): Modified: python/branches/release26-maint/Lib/io.py ============================================================================== --- python/branches/release26-maint/Lib/io.py (original) +++ python/branches/release26-maint/Lib/io.py Mon Aug 2 23:35:06 2010 @@ -849,8 +849,8 @@ if self.closed: raise ValueError("seek on closed file") try: - pos = pos.__index__() - except AttributeError as err: + pos.__index__ + except AttributeError: raise TypeError("an integer is required") # from err if whence == 0: if pos < 0: @@ -874,8 +874,13 @@ raise ValueError("truncate on closed file") if pos is None: pos = self._pos - elif pos < 0: - raise ValueError("negative truncate position %r" % (pos,)) + else: + try: + pos.__index__ + except AttributeError: + raise TypeError("an integer is required") + if pos < 0: + raise ValueError("negative truncate position %r" % (pos,)) del self._buffer[pos:] return pos @@ -1752,6 +1757,10 @@ if n is None: n = -1 decoder = self._decoder or self._get_decoder() + try: + n.__index__ + except AttributeError: + raise TypeError("an integer is required") if n < 0: # Read everything. result = (self._get_decoded_chars() + Modified: python/branches/release26-maint/Lib/warnings.py ============================================================================== --- python/branches/release26-maint/Lib/warnings.py (original) +++ python/branches/release26-maint/Lib/warnings.py Mon Aug 2 23:35:06 2010 @@ -29,7 +29,7 @@ file.write(formatwarning(message, category, filename, lineno, line)) except IOError: pass # the file (probably stderr) is invalid - this warning gets lost. -# Keep a worrking version around in case the deprecation of the old API is +# Keep a working version around in case the deprecation of the old API is # triggered. showwarning = _show_warning From python-checkins at python.org Mon Aug 2 23:36:12 2010 From: python-checkins at python.org (georg.brandl) Date: Mon, 2 Aug 2010 23:36:12 +0200 (CEST) Subject: [Python-checkins] r83589 - python/branches/release27-maint/Lib/pstats.py Message-ID: <20100802213612.BB8C3EE9F2@mail.python.org> Author: georg.brandl Date: Mon Aug 2 23:36:12 2010 New Revision: 83589 Log: Fix merging glitches. Modified: python/branches/release27-maint/Lib/pstats.py Modified: python/branches/release27-maint/Lib/pstats.py ============================================================================== --- python/branches/release27-maint/Lib/pstats.py (original) +++ python/branches/release27-maint/Lib/pstats.py Mon Aug 2 23:36:12 2010 @@ -150,7 +150,7 @@ if not arg_list: return self if len(arg_list) > 1: self.add(*arg_list[1:]) other = arg_list[0] - if type(self) != type(other): + if type(self) != type(other) or self.__class__ != other.__class__: other = Stats(other) self.files += other.files self.total_calls += other.total_calls @@ -218,7 +218,7 @@ if not field: self.fcn_list = 0 return self - if len(field) == 1 and isinstance(field[0], int): + if len(field) == 1 and isinstance(field[0], (int, long)): # Be compatible with old profiler field = [ {-1: "stdname", 0: "calls", @@ -300,7 +300,7 @@ def eval_print_amount(self, sel, list, msg): new_list = list - if isinstance(sel, str): + if isinstance(sel, basestring): try: rex = re.compile(sel) except re.error: @@ -315,7 +315,7 @@ if isinstance(sel, float) and 0.0 <= sel < 1.0: count = int(count * sel + .5) new_list = list[:count] - elif isinstance(sel, int) and 0 <= sel < count: + elif isinstance(sel, (int, long)) and 0 <= sel < count: count = sel new_list = list[:count] if len(list) != len(new_list): @@ -330,7 +330,7 @@ stat_list = self.fcn_list[:] msg = " Ordered by: " + self.sort_type + '\n' else: - stat_list = list(self.stats.keys()) + stat_list = self.stats.keys() msg = " Random listing order was used\n" for selection in sel_list: From python-checkins at python.org Mon Aug 2 23:38:08 2010 From: python-checkins at python.org (georg.brandl) Date: Mon, 2 Aug 2010 23:38:08 +0200 (CEST) Subject: [Python-checkins] r83590 - in python/branches/release27-maint: Lib/trace.py Message-ID: <20100802213808.696F8EEB96@mail.python.org> Author: georg.brandl Date: Mon Aug 2 23:38:08 2010 New Revision: 83590 Log: Merged revisions 83525 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83525 | georg.brandl | 2010-08-02 14:36:24 +0200 (Mo, 02 Aug 2010) | 1 line Get rid of spurious "threading" entries in trace output. ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Lib/trace.py Modified: python/branches/release27-maint/Lib/trace.py ============================================================================== --- python/branches/release27-maint/Lib/trace.py (original) +++ python/branches/release27-maint/Lib/trace.py Mon Aug 2 23:38:08 2010 @@ -494,8 +494,8 @@ import __main__ dict = __main__.__dict__ if not self.donothing: - sys.settrace(self.globaltrace) threading.settrace(self.globaltrace) + sys.settrace(self.globaltrace) try: exec cmd in dict, dict finally: @@ -507,8 +507,8 @@ if globals is None: globals = {} if locals is None: locals = {} if not self.donothing: - sys.settrace(self.globaltrace) threading.settrace(self.globaltrace) + sys.settrace(self.globaltrace) try: exec cmd in globals, locals finally: From python-checkins at python.org Mon Aug 2 23:38:31 2010 From: python-checkins at python.org (georg.brandl) Date: Mon, 2 Aug 2010 23:38:31 +0200 (CEST) Subject: [Python-checkins] r83591 - python/branches/release27-maint Message-ID: <20100802213831.202D7EEBE8@mail.python.org> Author: georg.brandl Date: Mon Aug 2 23:38:30 2010 New Revision: 83591 Log: Blocked revisions 83524,83526-83528 via svnmerge ........ r83524 | georg.brandl | 2010-08-02 14:20:23 +0200 (Mo, 02 Aug 2010) | 1 line #9428: fix running scripts from profile/cProfile with their own name and the right namespace. Same fix as for trace.py in #1690103. ........ r83526 | georg.brandl | 2010-08-02 14:40:22 +0200 (Mo, 02 Aug 2010) | 1 line Fix softspace relic. ........ r83527 | georg.brandl | 2010-08-02 14:48:46 +0200 (Mo, 02 Aug 2010) | 1 line #3821: beginnings of a trace.py unittest. ........ r83528 | georg.brandl | 2010-08-02 14:54:24 +0200 (Mo, 02 Aug 2010) | 1 line Document how to refer to decorators and decorator methods. ........ Modified: python/branches/release27-maint/ (props changed) From python-checkins at python.org Mon Aug 2 23:40:56 2010 From: python-checkins at python.org (ezio.melotti) Date: Mon, 2 Aug 2010 23:40:56 +0200 (CEST) Subject: [Python-checkins] r83592 - python/branches/release26-maint Message-ID: <20100802214056.5D637EEBE8@mail.python.org> Author: ezio.melotti Date: Mon Aug 2 23:40:56 2010 New Revision: 83592 Log: Unblocked revisions 78815 via svnmerge ........ r78815 | florent.xicluna | 2010-03-09 21:57:01 +0200 (Tue, 09 Mar 2010) | 2 lines #7772: Fix test_py3kwarn. Now the test suite could pass with "-3" flag. ........ Modified: python/branches/release26-maint/ (props changed) From python-checkins at python.org Mon Aug 2 23:44:26 2010 From: python-checkins at python.org (georg.brandl) Date: Mon, 2 Aug 2010 23:44:26 +0200 (CEST) Subject: [Python-checkins] r83593 - in python/branches/release27-maint: Doc/c-api/unicode.rst Doc/c-api/weakref.rst Doc/distutils/builtdist.rst Doc/extending/extending.rst Doc/library/cmd.rst Doc/library/functions.rst Doc/library/os.path.rst Doc/library/zipfile.rst Doc/reference/lexical_analysis.rst Doc/tutorial/classes.rst Lib/curses/wrapper.py Lib/distutils/command/bdist_msi.py Lib/distutils/command/bdist_wininst.py Modules/selectmodule.c PCbuild/readme.txt Message-ID: <20100802214426.22AD0EEC29@mail.python.org> Author: georg.brandl Date: Mon Aug 2 23:44:25 2010 New Revision: 83593 Log: Merged revisions 83536,83546-83548,83550,83554-83555,83558,83563,83565,83571,83574-83575 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83536 | georg.brandl | 2010-08-02 19:49:25 +0200 (Mo, 02 Aug 2010) | 1 line #8578: mention danger of not incref'ing weak referenced object. ........ r83546 | georg.brandl | 2010-08-02 21:16:34 +0200 (Mo, 02 Aug 2010) | 1 line #7973: Fix distutils options spelling. ........ r83547 | georg.brandl | 2010-08-02 21:19:26 +0200 (Mo, 02 Aug 2010) | 1 line #7386: add example that shows that trailing path separators are stripped. ........ r83548 | georg.brandl | 2010-08-02 21:23:34 +0200 (Mo, 02 Aug 2010) | 1 line #8172: how does one use a property? ........ r83550 | georg.brandl | 2010-08-02 21:32:43 +0200 (Mo, 02 Aug 2010) | 1 line #9451: strengthen warning about __*__ special name usage. ........ r83554 | georg.brandl | 2010-08-02 21:43:05 +0200 (Mo, 02 Aug 2010) | 1 line #7280: note about nasmw.exe. ........ r83555 | georg.brandl | 2010-08-02 21:44:48 +0200 (Mo, 02 Aug 2010) | 1 line #8861: remove unused variable. ........ r83558 | georg.brandl | 2010-08-02 22:05:19 +0200 (Mo, 02 Aug 2010) | 1 line #8648: document UTF-7 codec functions. ........ r83563 | georg.brandl | 2010-08-02 22:21:21 +0200 (Mo, 02 Aug 2010) | 1 line #9037: add example how to raise custom exceptions from C code. ........ r83565 | georg.brandl | 2010-08-02 22:27:20 +0200 (Mo, 02 Aug 2010) | 1 line #9111: document that do_help() looks at docstrings. ........ r83571 | georg.brandl | 2010-08-02 22:44:34 +0200 (Mo, 02 Aug 2010) | 1 line Clarify that abs() is not a namespace. ........ r83574 | georg.brandl | 2010-08-02 22:47:56 +0200 (Mo, 02 Aug 2010) | 1 line #6867: epoll.register() returns None. ........ r83575 | georg.brandl | 2010-08-02 22:52:10 +0200 (Mo, 02 Aug 2010) | 1 line #9238: zipfile does handle archive comments. ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Doc/c-api/unicode.rst python/branches/release27-maint/Doc/c-api/weakref.rst python/branches/release27-maint/Doc/distutils/builtdist.rst python/branches/release27-maint/Doc/extending/extending.rst python/branches/release27-maint/Doc/library/cmd.rst python/branches/release27-maint/Doc/library/functions.rst python/branches/release27-maint/Doc/library/os.path.rst python/branches/release27-maint/Doc/library/zipfile.rst python/branches/release27-maint/Doc/reference/lexical_analysis.rst python/branches/release27-maint/Doc/tutorial/classes.rst python/branches/release27-maint/Lib/curses/wrapper.py python/branches/release27-maint/Lib/distutils/command/bdist_msi.py python/branches/release27-maint/Lib/distutils/command/bdist_wininst.py python/branches/release27-maint/Modules/selectmodule.c python/branches/release27-maint/PCbuild/readme.txt Modified: python/branches/release27-maint/Doc/c-api/unicode.rst ============================================================================== --- python/branches/release27-maint/Doc/c-api/unicode.rst (original) +++ python/branches/release27-maint/Doc/c-api/unicode.rst Mon Aug 2 23:44:25 2010 @@ -567,6 +567,38 @@ *NULL* if an exception was raised by the codec. +UTF-7 Codecs +"""""""""""" + +These are the UTF-7 codec APIs: + + +.. cfunction:: PyObject* PyUnicode_DecodeUTF7(const char *s, Py_ssize_t size, const char *errors) + + Create a Unicode object by decoding *size* bytes of the UTF-7 encoded string + *s*. Return *NULL* if an exception was raised by the codec. + + +.. cfunction:: PyObject* PyUnicode_DecodeUTF8Stateful(const char *s, Py_ssize_t size, const char *errors, Py_ssize_t *consumed) + + If *consumed* is *NULL*, behave like :cfunc:`PyUnicode_DecodeUTF7`. If + *consumed* is not *NULL*, trailing incomplete UTF-7 base-64 sections will not + be treated as an error. Those bytes will not be decoded and the number of + bytes that have been decoded will be stored in *consumed*. + + +.. cfunction:: PyObject* PyUnicode_EncodeUTF7(const Py_UNICODE *s, Py_ssize_t size, int base64SetO, int base64WhiteSpace, const char *errors) + + Encode the :ctype:`Py_UNICODE` buffer of the given size using UTF-7 and + return a Python bytes object. Return *NULL* if an exception was raised by + the codec. + + If *base64SetO* is nonzero, "Set O" (punctuation that has no otherwise + special meaning) will be encoded in base-64. If *base64WhiteSpace* is + nonzero, whitespace will be encoded in base-64. Both are set to zero for the + Python "utf-7" codec. + + Unicode-Escape Codecs """"""""""""""""""""" Modified: python/branches/release27-maint/Doc/c-api/weakref.rst ============================================================================== --- python/branches/release27-maint/Doc/c-api/weakref.rst (original) +++ python/branches/release27-maint/Doc/c-api/weakref.rst Mon Aug 2 23:44:25 2010 @@ -63,10 +63,17 @@ .. cfunction:: PyObject* PyWeakref_GetObject(PyObject *ref) Return the referenced object from a weak reference, *ref*. If the referent is - no longer live, returns ``None``. + no longer live, returns :const:`Py_None`. .. versionadded:: 2.2 + .. warning:: + + This function returns a **borrowed reference** to the referenced object. + This means that you should always call :cfunc:`Py_INCREF` on the object + except if you know that it cannot be destroyed while you are still + using it. + .. cfunction:: PyObject* PyWeakref_GET_OBJECT(PyObject *ref) Modified: python/branches/release27-maint/Doc/distutils/builtdist.rst ============================================================================== --- python/branches/release27-maint/Doc/distutils/builtdist.rst (original) +++ python/branches/release27-maint/Doc/distutils/builtdist.rst Mon Aug 2 23:44:25 2010 @@ -176,7 +176,7 @@ explicitly specify multiple :command:`bdist_\*` commands and their options:: python setup.py bdist_rpm --packager="John Doe " \ - bdist_wininst --target_version="2.0" + bdist_wininst --target-version="2.0" Creating RPM packages is driven by a :file:`.spec` file, much as using the Distutils is driven by the setup script. To make your life easier, the Modified: python/branches/release27-maint/Doc/extending/extending.rst ============================================================================== --- python/branches/release27-maint/Doc/extending/extending.rst (original) +++ python/branches/release27-maint/Doc/extending/extending.rst Mon Aug 2 23:44:25 2010 @@ -228,9 +228,28 @@ become a dangling pointer. Should it become a dangling pointer, C code which raises the exception could cause a core dump or other unintended side effects. -We discuss the use of PyMODINIT_FUNC as a function return type later in this +We discuss the use of ``PyMODINIT_FUNC`` as a function return type later in this sample. +The :exc:`spam.error` exception can be raised in your extension module using a +call to :cfunc:`PyErr_SetString` as shown below:: + + static PyObject * + spam_system(PyObject *self, PyObject *args) + { + const char *command; + int sts; + + if (!PyArg_ParseTuple(args, "s", &command)) + return NULL; + sts = system(command); + if (sts < 0) { + PyErr_SetString(SpamError, "System command failed"); + return NULL; + } + return PyLong_FromLong(sts); + } + .. _backtoexample: Modified: python/branches/release27-maint/Doc/library/cmd.rst ============================================================================== --- python/branches/release27-maint/Doc/library/cmd.rst (original) +++ python/branches/release27-maint/Doc/library/cmd.rst Mon Aug 2 23:44:25 2010 @@ -80,11 +80,13 @@ are the beginning and ending indexes of the prefix text, which could be used to provide different completion depending upon which position the argument is in. - All subclasses of :class:`Cmd` inherit a predefined :meth:`do_help`. This + All subclasses of :class:`Cmd` inherit a predefined :meth:`do_help`. This method, called with an argument ``'bar'``, invokes the corresponding method - :meth:`help_bar`. With no argument, :meth:`do_help` lists all available help - topics (that is, all commands with corresponding :meth:`help_\*` methods), and - also lists any undocumented commands. + :meth:`help_bar`, and if that is not present, prints the docstring of + :meth:`do_bar`, if available. With no argument, :meth:`do_help` lists all + available help topics (that is, all commands with corresponding + :meth:`help_\*` methods or commands that have docstrings), and also lists any + undocumented commands. .. method:: Cmd.onecmd(str) Modified: python/branches/release27-maint/Doc/library/functions.rst ============================================================================== --- python/branches/release27-maint/Doc/library/functions.rst (original) +++ python/branches/release27-maint/Doc/library/functions.rst Mon Aug 2 23:44:25 2010 @@ -874,7 +874,7 @@ *fget* is a function for getting an attribute value, likewise *fset* is a function for setting, and *fdel* a function for del'ing, an attribute. Typical - use is to define a managed attribute x:: + use is to define a managed attribute ``x``:: class C(object): def __init__(self): @@ -888,6 +888,9 @@ del self._x x = property(getx, setx, delx, "I'm the 'x' property.") + If then *c* is an instance of *C*, ``c.x`` will invoke the getter, + ``c.x = value`` will invoke the setter and ``del c.x`` the deleter. + If given, *doc* will be the docstring of the property attribute. Otherwise, the property will copy *fget*'s docstring (if it exists). This makes it possible to create read-only properties easily using :func:`property` as a :term:`decorator`:: Modified: python/branches/release27-maint/Doc/library/os.path.rst ============================================================================== --- python/branches/release27-maint/Doc/library/os.path.rst (original) +++ python/branches/release27-maint/Doc/library/os.path.rst Mon Aug 2 23:44:25 2010 @@ -212,7 +212,9 @@ .. function:: normpath(path) Normalize a pathname. This collapses redundant separators and up-level - references so that ``A//B``, ``A/./B`` and ``A/foo/../B`` all become ``A/B``. + references so that ``A//B``, ``A/B/``, ``A/./B`` and ``A/foo/../B`` all become + ``A/B``. + It does not normalize the case (use :func:`normcase` for that). On Windows, it converts forward slashes to backward slashes. It should be understood that this may change the meaning of the path if it contains symbolic links! Modified: python/branches/release27-maint/Doc/library/zipfile.rst ============================================================================== --- python/branches/release27-maint/Doc/library/zipfile.rst (original) +++ python/branches/release27-maint/Doc/library/zipfile.rst Mon Aug 2 23:44:25 2010 @@ -15,10 +15,8 @@ defined in `PKZIP Application Note `_. -This module does not currently handle multi-disk ZIP files, or ZIP files -which have appended comments (although it correctly handles comments -added to individual archive members---for which see the :ref:`zipinfo-objects` -documentation). It can handle ZIP files that use the ZIP64 extensions +This module does not currently handle multi-disk ZIP files. +It can handle ZIP files that use the ZIP64 extensions (that is ZIP files that are more than 4 GByte in size). It supports decryption of encrypted files in ZIP archives, but it currently cannot create an encrypted file. Decryption is extremely slow as it is @@ -67,7 +65,6 @@ Returns ``True`` if *filename* is a valid ZIP file based on its magic number, otherwise returns ``False``. *filename* may be a file or file-like object too. - This module does not currently handle ZIP files which have appended comments. .. versionchanged:: 2.7 Support for file and file-like objects. Modified: python/branches/release27-maint/Doc/reference/lexical_analysis.rst ============================================================================== --- python/branches/release27-maint/Doc/reference/lexical_analysis.rst (original) +++ python/branches/release27-maint/Doc/reference/lexical_analysis.rst Mon Aug 2 23:44:25 2010 @@ -386,11 +386,12 @@ information on this convention. ``__*__`` - System-defined names. These names are defined by the interpreter and its - implementation (including the standard library); applications should not expect - to define additional names using this convention. The set of names of this - class defined by Python may be extended in future versions. See section - :ref:`specialnames`. + System-defined names. These names are defined by the interpreter and its + implementation (including the standard library). Current system names are + discussed in the :ref:`specialnames` section and elsewhere. More will likely + be defined in future versions of Python. *Any* use of ``__*__`` names, in + any context, that does not follow explicitly documented use, is subject to + breakage without warning. ``__*`` Class-private names. Names in this category, when used within the context of a Modified: python/branches/release27-maint/Doc/tutorial/classes.rst ============================================================================== --- python/branches/release27-maint/Doc/tutorial/classes.rst (original) +++ python/branches/release27-maint/Doc/tutorial/classes.rst Mon Aug 2 23:44:25 2010 @@ -64,7 +64,7 @@ A *namespace* is a mapping from names to objects. Most namespaces are currently implemented as Python dictionaries, but that's normally not noticeable in any way (except for performance), and it may change in the future. Examples of -namespaces are: the set of built-in names (functions such as :func:`abs`, and +namespaces are: the set of built-in names (containing functions such as :func:`abs`, and built-in exception names); the global names in a module; and the local names in a function invocation. In a sense the set of attributes of an object also form a namespace. The important thing to know about namespaces is that there is Modified: python/branches/release27-maint/Lib/curses/wrapper.py ============================================================================== --- python/branches/release27-maint/Lib/curses/wrapper.py (original) +++ python/branches/release27-maint/Lib/curses/wrapper.py Mon Aug 2 23:44:25 2010 @@ -17,10 +17,9 @@ wrapper(). """ - res = None try: # Initialize curses - stdscr=curses.initscr() + stdscr = curses.initscr() # Turn off echoing of keys, and enter cbreak mode, # where no buffering is performed on keyboard input Modified: python/branches/release27-maint/Lib/distutils/command/bdist_msi.py ============================================================================== --- python/branches/release27-maint/Lib/distutils/command/bdist_msi.py (original) +++ python/branches/release27-maint/Lib/distutils/command/bdist_msi.py Mon Aug 2 23:44:25 2010 @@ -148,7 +148,7 @@ if not self.skip_build and self.distribution.has_ext_modules()\ and self.target_version != short_version: raise DistutilsOptionError, \ - "target version can only be %s, or the '--skip_build'" \ + "target version can only be %s, or the '--skip-build'" \ " option must be specified" % (short_version,) else: self.versions = list(self.all_versions) Modified: python/branches/release27-maint/Lib/distutils/command/bdist_wininst.py ============================================================================== --- python/branches/release27-maint/Lib/distutils/command/bdist_wininst.py (original) +++ python/branches/release27-maint/Lib/distutils/command/bdist_wininst.py Mon Aug 2 23:44:25 2010 @@ -95,7 +95,7 @@ short_version = get_python_version() if self.target_version and self.target_version != short_version: raise DistutilsOptionError, \ - "target version can only be %s, or the '--skip_build'" \ + "target version can only be %s, or the '--skip-build'" \ " option must be specified" % (short_version,) self.target_version = short_version Modified: python/branches/release27-maint/Modules/selectmodule.c ============================================================================== --- python/branches/release27-maint/Modules/selectmodule.c (original) +++ python/branches/release27-maint/Modules/selectmodule.c Mon Aug 2 23:44:25 2010 @@ -910,10 +910,9 @@ } PyDoc_STRVAR(pyepoll_register_doc, -"register(fd[, eventmask]) -> bool\n\ +"register(fd[, eventmask]) -> None\n\ \n\ -Registers a new fd or modifies an already registered fd. register() returns\n\ -True if a new fd was registered or False if the event mask for fd was modified.\n\ +Registers a new fd or modifies an already registered fd.\n\ fd is the target file descriptor of the operation.\n\ events is a bit set composed of the various EPOLL constants; the default\n\ is EPOLL_IN | EPOLL_OUT | EPOLL_PRI.\n\ Modified: python/branches/release27-maint/PCbuild/readme.txt ============================================================================== --- python/branches/release27-maint/PCbuild/readme.txt (original) +++ python/branches/release27-maint/PCbuild/readme.txt Mon Aug 2 23:44:25 2010 @@ -155,6 +155,8 @@ You must install the NASM assembler from http://nasm.sf.net for x86 builds. Put nasmw.exe anywhere in your PATH. + Note: recent releases of nasm only have nasm.exe. Just rename it to + nasmw.exe. You can also install ActivePerl from http://www.activestate.com/Products/ActivePerl/ From python-checkins at python.org Mon Aug 2 23:45:44 2010 From: python-checkins at python.org (georg.brandl) Date: Mon, 2 Aug 2010 23:45:44 +0200 (CEST) Subject: [Python-checkins] r83594 - in python/branches/release26-maint: Doc/c-api/unicode.rst Doc/c-api/weakref.rst Doc/distutils/builtdist.rst Doc/extending/extending.rst Doc/library/cmd.rst Doc/library/functions.rst Doc/library/os.path.rst Doc/library/zipfile.rst Doc/reference/lexical_analysis.rst Doc/tutorial/classes.rst Lib/curses/wrapper.py Lib/distutils/command/bdist_msi.py Lib/distutils/command/bdist_wininst.py Modules/selectmodule.c PCbuild/readme.txt Message-ID: <20100802214544.01E6AEEC07@mail.python.org> Author: georg.brandl Date: Mon Aug 2 23:45:43 2010 New Revision: 83594 Log: Merged revisions 83593 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/release27-maint ................ r83593 | georg.brandl | 2010-08-02 23:44:25 +0200 (Mo, 02 Aug 2010) | 57 lines Merged revisions 83536,83546-83548,83550,83554-83555,83558,83563,83565,83571,83574-83575 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83536 | georg.brandl | 2010-08-02 19:49:25 +0200 (Mo, 02 Aug 2010) | 1 line #8578: mention danger of not incref'ing weak referenced object. ........ r83546 | georg.brandl | 2010-08-02 21:16:34 +0200 (Mo, 02 Aug 2010) | 1 line #7973: Fix distutils options spelling. ........ r83547 | georg.brandl | 2010-08-02 21:19:26 +0200 (Mo, 02 Aug 2010) | 1 line #7386: add example that shows that trailing path separators are stripped. ........ r83548 | georg.brandl | 2010-08-02 21:23:34 +0200 (Mo, 02 Aug 2010) | 1 line #8172: how does one use a property? ........ r83550 | georg.brandl | 2010-08-02 21:32:43 +0200 (Mo, 02 Aug 2010) | 1 line #9451: strengthen warning about __*__ special name usage. ........ r83554 | georg.brandl | 2010-08-02 21:43:05 +0200 (Mo, 02 Aug 2010) | 1 line #7280: note about nasmw.exe. ........ r83555 | georg.brandl | 2010-08-02 21:44:48 +0200 (Mo, 02 Aug 2010) | 1 line #8861: remove unused variable. ........ r83558 | georg.brandl | 2010-08-02 22:05:19 +0200 (Mo, 02 Aug 2010) | 1 line #8648: document UTF-7 codec functions. ........ r83563 | georg.brandl | 2010-08-02 22:21:21 +0200 (Mo, 02 Aug 2010) | 1 line #9037: add example how to raise custom exceptions from C code. ........ r83565 | georg.brandl | 2010-08-02 22:27:20 +0200 (Mo, 02 Aug 2010) | 1 line #9111: document that do_help() looks at docstrings. ........ r83571 | georg.brandl | 2010-08-02 22:44:34 +0200 (Mo, 02 Aug 2010) | 1 line Clarify that abs() is not a namespace. ........ r83574 | georg.brandl | 2010-08-02 22:47:56 +0200 (Mo, 02 Aug 2010) | 1 line #6867: epoll.register() returns None. ........ r83575 | georg.brandl | 2010-08-02 22:52:10 +0200 (Mo, 02 Aug 2010) | 1 line #9238: zipfile does handle archive comments. ........ ................ Modified: python/branches/release26-maint/ (props changed) python/branches/release26-maint/Doc/c-api/unicode.rst python/branches/release26-maint/Doc/c-api/weakref.rst python/branches/release26-maint/Doc/distutils/builtdist.rst python/branches/release26-maint/Doc/extending/extending.rst python/branches/release26-maint/Doc/library/cmd.rst python/branches/release26-maint/Doc/library/functions.rst python/branches/release26-maint/Doc/library/os.path.rst python/branches/release26-maint/Doc/library/zipfile.rst python/branches/release26-maint/Doc/reference/lexical_analysis.rst python/branches/release26-maint/Doc/tutorial/classes.rst python/branches/release26-maint/Lib/curses/wrapper.py python/branches/release26-maint/Lib/distutils/command/bdist_msi.py python/branches/release26-maint/Lib/distutils/command/bdist_wininst.py python/branches/release26-maint/Modules/selectmodule.c python/branches/release26-maint/PCbuild/readme.txt Modified: python/branches/release26-maint/Doc/c-api/unicode.rst ============================================================================== --- python/branches/release26-maint/Doc/c-api/unicode.rst (original) +++ python/branches/release26-maint/Doc/c-api/unicode.rst Mon Aug 2 23:45:43 2010 @@ -567,6 +567,38 @@ *NULL* if an exception was raised by the codec. +UTF-7 Codecs +"""""""""""" + +These are the UTF-7 codec APIs: + + +.. cfunction:: PyObject* PyUnicode_DecodeUTF7(const char *s, Py_ssize_t size, const char *errors) + + Create a Unicode object by decoding *size* bytes of the UTF-7 encoded string + *s*. Return *NULL* if an exception was raised by the codec. + + +.. cfunction:: PyObject* PyUnicode_DecodeUTF8Stateful(const char *s, Py_ssize_t size, const char *errors, Py_ssize_t *consumed) + + If *consumed* is *NULL*, behave like :cfunc:`PyUnicode_DecodeUTF7`. If + *consumed* is not *NULL*, trailing incomplete UTF-7 base-64 sections will not + be treated as an error. Those bytes will not be decoded and the number of + bytes that have been decoded will be stored in *consumed*. + + +.. cfunction:: PyObject* PyUnicode_EncodeUTF7(const Py_UNICODE *s, Py_ssize_t size, int base64SetO, int base64WhiteSpace, const char *errors) + + Encode the :ctype:`Py_UNICODE` buffer of the given size using UTF-7 and + return a Python bytes object. Return *NULL* if an exception was raised by + the codec. + + If *base64SetO* is nonzero, "Set O" (punctuation that has no otherwise + special meaning) will be encoded in base-64. If *base64WhiteSpace* is + nonzero, whitespace will be encoded in base-64. Both are set to zero for the + Python "utf-7" codec. + + Unicode-Escape Codecs """"""""""""""""""""" Modified: python/branches/release26-maint/Doc/c-api/weakref.rst ============================================================================== --- python/branches/release26-maint/Doc/c-api/weakref.rst (original) +++ python/branches/release26-maint/Doc/c-api/weakref.rst Mon Aug 2 23:45:43 2010 @@ -63,10 +63,17 @@ .. cfunction:: PyObject* PyWeakref_GetObject(PyObject *ref) Return the referenced object from a weak reference, *ref*. If the referent is - no longer live, returns ``None``. + no longer live, returns :const:`Py_None`. .. versionadded:: 2.2 + .. warning:: + + This function returns a **borrowed reference** to the referenced object. + This means that you should always call :cfunc:`Py_INCREF` on the object + except if you know that it cannot be destroyed while you are still + using it. + .. cfunction:: PyObject* PyWeakref_GET_OBJECT(PyObject *ref) Modified: python/branches/release26-maint/Doc/distutils/builtdist.rst ============================================================================== --- python/branches/release26-maint/Doc/distutils/builtdist.rst (original) +++ python/branches/release26-maint/Doc/distutils/builtdist.rst Mon Aug 2 23:45:43 2010 @@ -173,7 +173,7 @@ explicitly specify multiple :command:`bdist_\*` commands and their options:: python setup.py bdist_rpm --packager="John Doe " \ - bdist_wininst --target_version="2.0" + bdist_wininst --target-version="2.0" Creating RPM packages is driven by a :file:`.spec` file, much as using the Distutils is driven by the setup script. To make your life easier, the Modified: python/branches/release26-maint/Doc/extending/extending.rst ============================================================================== --- python/branches/release26-maint/Doc/extending/extending.rst (original) +++ python/branches/release26-maint/Doc/extending/extending.rst Mon Aug 2 23:45:43 2010 @@ -228,9 +228,28 @@ become a dangling pointer. Should it become a dangling pointer, C code which raises the exception could cause a core dump or other unintended side effects. -We discuss the use of PyMODINIT_FUNC as a function return type later in this +We discuss the use of ``PyMODINIT_FUNC`` as a function return type later in this sample. +The :exc:`spam.error` exception can be raised in your extension module using a +call to :cfunc:`PyErr_SetString` as shown below:: + + static PyObject * + spam_system(PyObject *self, PyObject *args) + { + const char *command; + int sts; + + if (!PyArg_ParseTuple(args, "s", &command)) + return NULL; + sts = system(command); + if (sts < 0) { + PyErr_SetString(SpamError, "System command failed"); + return NULL; + } + return PyLong_FromLong(sts); + } + .. _backtoexample: Modified: python/branches/release26-maint/Doc/library/cmd.rst ============================================================================== --- python/branches/release26-maint/Doc/library/cmd.rst (original) +++ python/branches/release26-maint/Doc/library/cmd.rst Mon Aug 2 23:45:43 2010 @@ -80,11 +80,13 @@ are the beginning and ending indexes of the prefix text, which could be used to provide different completion depending upon which position the argument is in. - All subclasses of :class:`Cmd` inherit a predefined :meth:`do_help`. This + All subclasses of :class:`Cmd` inherit a predefined :meth:`do_help`. This method, called with an argument ``'bar'``, invokes the corresponding method - :meth:`help_bar`. With no argument, :meth:`do_help` lists all available help - topics (that is, all commands with corresponding :meth:`help_\*` methods), and - also lists any undocumented commands. + :meth:`help_bar`, and if that is not present, prints the docstring of + :meth:`do_bar`, if available. With no argument, :meth:`do_help` lists all + available help topics (that is, all commands with corresponding + :meth:`help_\*` methods or commands that have docstrings), and also lists any + undocumented commands. .. method:: Cmd.onecmd(str) Modified: python/branches/release26-maint/Doc/library/functions.rst ============================================================================== --- python/branches/release26-maint/Doc/library/functions.rst (original) +++ python/branches/release26-maint/Doc/library/functions.rst Mon Aug 2 23:45:43 2010 @@ -864,7 +864,7 @@ *fget* is a function for getting an attribute value, likewise *fset* is a function for setting, and *fdel* a function for del'ing, an attribute. Typical - use is to define a managed attribute x:: + use is to define a managed attribute ``x``:: class C(object): def __init__(self): @@ -878,6 +878,9 @@ del self._x x = property(getx, setx, delx, "I'm the 'x' property.") + If then *c* is an instance of *C*, ``c.x`` will invoke the getter, + ``c.x = value`` will invoke the setter and ``del c.x`` the deleter. + If given, *doc* will be the docstring of the property attribute. Otherwise, the property will copy *fget*'s docstring (if it exists). This makes it possible to create read-only properties easily using :func:`property` as a :term:`decorator`:: Modified: python/branches/release26-maint/Doc/library/os.path.rst ============================================================================== --- python/branches/release26-maint/Doc/library/os.path.rst (original) +++ python/branches/release26-maint/Doc/library/os.path.rst Mon Aug 2 23:45:43 2010 @@ -212,7 +212,9 @@ .. function:: normpath(path) Normalize a pathname. This collapses redundant separators and up-level - references so that ``A//B``, ``A/./B`` and ``A/foo/../B`` all become ``A/B``. + references so that ``A//B``, ``A/B/``, ``A/./B`` and ``A/foo/../B`` all become + ``A/B``. + It does not normalize the case (use :func:`normcase` for that). On Windows, it converts forward slashes to backward slashes. It should be understood that this may change the meaning of the path if it contains symbolic links! Modified: python/branches/release26-maint/Doc/library/zipfile.rst ============================================================================== --- python/branches/release26-maint/Doc/library/zipfile.rst (original) +++ python/branches/release26-maint/Doc/library/zipfile.rst Mon Aug 2 23:45:43 2010 @@ -15,10 +15,8 @@ defined in `PKZIP Application Note `_. -This module does not currently handle multi-disk ZIP files, or ZIP files -which have appended comments (although it correctly handles comments -added to individual archive members---for which see the :ref:`zipinfo-objects` -documentation). It can handle ZIP files that use the ZIP64 extensions +This module does not currently handle multi-disk ZIP files. +It can handle ZIP files that use the ZIP64 extensions (that is ZIP files that are more than 4 GByte in size). It supports decryption of encrypted files in ZIP archives, but it currently cannot create an encrypted file. Decryption is extremely slow as it is @@ -66,8 +64,7 @@ .. function:: is_zipfile(filename) Returns ``True`` if *filename* is a valid ZIP file based on its magic number, - otherwise returns ``False``. This module does not currently handle ZIP files - which have appended comments. + otherwise returns ``False``. .. data:: ZIP_STORED Modified: python/branches/release26-maint/Doc/reference/lexical_analysis.rst ============================================================================== --- python/branches/release26-maint/Doc/reference/lexical_analysis.rst (original) +++ python/branches/release26-maint/Doc/reference/lexical_analysis.rst Mon Aug 2 23:45:43 2010 @@ -386,11 +386,12 @@ information on this convention. ``__*__`` - System-defined names. These names are defined by the interpreter and its - implementation (including the standard library); applications should not expect - to define additional names using this convention. The set of names of this - class defined by Python may be extended in future versions. See section - :ref:`specialnames`. + System-defined names. These names are defined by the interpreter and its + implementation (including the standard library). Current system names are + discussed in the :ref:`specialnames` section and elsewhere. More will likely + be defined in future versions of Python. *Any* use of ``__*__`` names, in + any context, that does not follow explicitly documented use, is subject to + breakage without warning. ``__*`` Class-private names. Names in this category, when used within the context of a Modified: python/branches/release26-maint/Doc/tutorial/classes.rst ============================================================================== --- python/branches/release26-maint/Doc/tutorial/classes.rst (original) +++ python/branches/release26-maint/Doc/tutorial/classes.rst Mon Aug 2 23:45:43 2010 @@ -64,7 +64,7 @@ A *namespace* is a mapping from names to objects. Most namespaces are currently implemented as Python dictionaries, but that's normally not noticeable in any way (except for performance), and it may change in the future. Examples of -namespaces are: the set of built-in names (functions such as :func:`abs`, and +namespaces are: the set of built-in names (containing functions such as :func:`abs`, and built-in exception names); the global names in a module; and the local names in a function invocation. In a sense the set of attributes of an object also form a namespace. The important thing to know about namespaces is that there is Modified: python/branches/release26-maint/Lib/curses/wrapper.py ============================================================================== --- python/branches/release26-maint/Lib/curses/wrapper.py (original) +++ python/branches/release26-maint/Lib/curses/wrapper.py Mon Aug 2 23:45:43 2010 @@ -17,10 +17,9 @@ wrapper(). """ - res = None try: # Initialize curses - stdscr=curses.initscr() + stdscr = curses.initscr() # Turn off echoing of keys, and enter cbreak mode, # where no buffering is performed on keyboard input Modified: python/branches/release26-maint/Lib/distutils/command/bdist_msi.py ============================================================================== --- python/branches/release26-maint/Lib/distutils/command/bdist_msi.py (original) +++ python/branches/release26-maint/Lib/distutils/command/bdist_msi.py Mon Aug 2 23:45:43 2010 @@ -138,7 +138,7 @@ if not self.skip_build and self.distribution.has_ext_modules()\ and self.target_version != short_version: raise DistutilsOptionError, \ - "target version can only be %s, or the '--skip_build'" \ + "target version can only be %s, or the '--skip-build'" \ " option must be specified" % (short_version,) else: self.target_version = short_version Modified: python/branches/release26-maint/Lib/distutils/command/bdist_wininst.py ============================================================================== --- python/branches/release26-maint/Lib/distutils/command/bdist_wininst.py (original) +++ python/branches/release26-maint/Lib/distutils/command/bdist_wininst.py Mon Aug 2 23:45:43 2010 @@ -93,7 +93,7 @@ short_version = get_python_version() if self.target_version and self.target_version != short_version: raise DistutilsOptionError, \ - "target version can only be %s, or the '--skip_build'" \ + "target version can only be %s, or the '--skip-build'" \ " option must be specified" % (short_version,) self.target_version = short_version Modified: python/branches/release26-maint/Modules/selectmodule.c ============================================================================== --- python/branches/release26-maint/Modules/selectmodule.c (original) +++ python/branches/release26-maint/Modules/selectmodule.c Mon Aug 2 23:45:43 2010 @@ -910,10 +910,9 @@ } PyDoc_STRVAR(pyepoll_register_doc, -"register(fd[, eventmask]) -> bool\n\ +"register(fd[, eventmask]) -> None\n\ \n\ -Registers a new fd or modifies an already registered fd. register() returns\n\ -True if a new fd was registered or False if the event mask for fd was modified.\n\ +Registers a new fd or modifies an already registered fd.\n\ fd is the target file descriptor of the operation.\n\ events is a bit set composed of the various EPOLL constants; the default\n\ is EPOLL_IN | EPOLL_OUT | EPOLL_PRI.\n\ Modified: python/branches/release26-maint/PCbuild/readme.txt ============================================================================== --- python/branches/release26-maint/PCbuild/readme.txt (original) +++ python/branches/release26-maint/PCbuild/readme.txt Mon Aug 2 23:45:43 2010 @@ -155,6 +155,8 @@ You must install the NASM assembler from http://nasm.sf.net for x86 builds. Put nasmw.exe anywhere in your PATH. + Note: recent releases of nasm only have nasm.exe. Just rename it to + nasmw.exe. You can also install ActivePerl from http://www.activestate.com/Products/ActivePerl/ From python-checkins at python.org Mon Aug 2 23:46:19 2010 From: python-checkins at python.org (georg.brandl) Date: Mon, 2 Aug 2010 23:46:19 +0200 (CEST) Subject: [Python-checkins] r83595 - python/branches/release27-maint Message-ID: <20100802214619.B86DBEEA25@mail.python.org> Author: georg.brandl Date: Mon Aug 2 23:46:19 2010 New Revision: 83595 Log: Blocked revisions 83538,83542,83560-83561,83569,83580,83584 via svnmerge ........ r83538 | georg.brandl | 2010-08-02 20:10:13 +0200 (Mo, 02 Aug 2010) | 1 line #6928: fix class docs w.r.t. new metaclasses. ........ r83542 | georg.brandl | 2010-08-02 20:56:54 +0200 (Mo, 02 Aug 2010) | 1 line Move test_SimpleHTTPServer into test_httpservers. ........ r83560 | georg.brandl | 2010-08-02 22:16:18 +0200 (Mo, 02 Aug 2010) | 1 line #9087: update json docstrings -- unicode and long do not exist anymore. ........ r83561 | georg.brandl | 2010-08-02 22:17:50 +0200 (Mo, 02 Aug 2010) | 1 line #4280: remove outdated "versionchecker" tool. ........ r83569 | georg.brandl | 2010-08-02 22:39:35 +0200 (Mo, 02 Aug 2010) | 1 line #7797: be explicit about bytes-oriented interface of base64 functions. ........ r83580 | georg.brandl | 2010-08-02 23:02:36 +0200 (Mo, 02 Aug 2010) | 1 line #8119: fix copy-paste error. ........ r83584 | georg.brandl | 2010-08-02 23:07:14 +0200 (Mo, 02 Aug 2010) | 1 line #9457: fix documentation links for 3.2. ........ Modified: python/branches/release27-maint/ (props changed) From python-checkins at python.org Mon Aug 2 23:47:04 2010 From: python-checkins at python.org (georg.brandl) Date: Mon, 2 Aug 2010 23:47:04 +0200 (CEST) Subject: [Python-checkins] r83596 - in python/branches/release26-maint: Doc/library/constants.rst Message-ID: <20100802214704.85C8AEEC29@mail.python.org> Author: georg.brandl Date: Mon Aug 2 23:47:02 2010 New Revision: 83596 Log: Merged revisions 83567 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/release27-maint ................ r83567 | georg.brandl | 2010-08-02 22:32:03 +0200 (Mo, 02 Aug 2010) | 9 lines Merged revisions 83552 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83552 | georg.brandl | 2010-08-02 21:36:36 +0200 (Mo, 02 Aug 2010) | 1 line #9438: clarify that constant names also cannot be assigned as attributes. ........ ................ Modified: python/branches/release26-maint/ (props changed) python/branches/release26-maint/Doc/library/constants.rst Modified: python/branches/release26-maint/Doc/library/constants.rst ============================================================================== --- python/branches/release26-maint/Doc/library/constants.rst (original) +++ python/branches/release26-maint/Doc/library/constants.rst Mon Aug 2 23:47:02 2010 @@ -3,7 +3,6 @@ A small number of constants live in the built-in namespace. They are: - .. data:: False The false value of the :class:`bool` type. @@ -39,16 +38,23 @@ Special value used in conjunction with extended slicing syntax. - .. XXX Someone who understands extended slicing should fill in here. - .. data:: __debug__ This constant is true if Python was not started with an :option:`-O` option. - Assignments to :const:`__debug__` are illegal and raise a :exc:`SyntaxError`. See also the :keyword:`assert` statement. +.. note:: + + The names :data:`None` and :data:`__debug__` cannot be reassigned + (assignments to them, even as an attribute name, raise :exc:`SyntaxError`), + so they can be considered "true" constants. + + .. versionchanged:: 2.7 + Assignments to ``__debug__`` as an attribute became illegal. + + Constants added by the :mod:`site` module ----------------------------------------- From python-checkins at python.org Mon Aug 2 23:48:40 2010 From: python-checkins at python.org (ezio.melotti) Date: Mon, 2 Aug 2010 23:48:40 +0200 (CEST) Subject: [Python-checkins] r83597 - in python/branches/release26-maint: Lib/test/test_py3kwarn.py Lib/test/test_support.py Message-ID: <20100802214840.24FA0EE9AD@mail.python.org> Author: ezio.melotti Date: Mon Aug 2 23:48:39 2010 New Revision: 83597 Log: Merged revisions 78815 via svnmerge from svn+ssh://pythondev at svn.python.org/python/trunk ........ r78815 | florent.xicluna | 2010-03-09 21:57:01 +0200 (Tue, 09 Mar 2010) | 2 lines #7772: Fix test_py3kwarn. Now the test suite could pass with "-3" flag. ........ Modified: python/branches/release26-maint/ (props changed) python/branches/release26-maint/Lib/test/test_py3kwarn.py python/branches/release26-maint/Lib/test/test_support.py Modified: python/branches/release26-maint/Lib/test/test_py3kwarn.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_py3kwarn.py (original) +++ python/branches/release26-maint/Lib/test/test_py3kwarn.py Mon Aug 2 23:48:39 2010 @@ -9,6 +9,23 @@ if not sys.py3kwarning: raise TestSkipped('%s must be run with the -3 flag' % __name__) +try: + from test.test_support import __warningregistry__ as _registry +except ImportError: + def check_deprecated_module(module_name): + return False +else: + past_warnings = _registry.keys() + del _registry + def check_deprecated_module(module_name): + """Lookup the past warnings for module already loaded using + test_support.import_module(..., deprecated=True) + """ + return any(module_name in msg and ' removed' in msg + and issubclass(cls, DeprecationWarning) + and (' module' in msg or ' package' in msg) + for (msg, cls, line) in past_warnings) + def reset_module_registry(module): try: registry = module.__warningregistry__ @@ -331,10 +348,9 @@ """Make sure the specified module, when imported, raises a DeprecationWarning and specifies itself in the message.""" with nested(CleanImport(module_name), warnings.catch_warnings()): - # XXX: This is not quite enough for extension modules - those - # won't rerun their init code even with CleanImport. - # You can see this easily by running the whole test suite with -3 - warnings.filterwarnings("error", ".+ removed", + warnings.filterwarnings("error", ".+ (module|package) .+ removed", + DeprecationWarning, __name__) + warnings.filterwarnings("error", ".+ removed .+ (module|package)", DeprecationWarning, __name__) try: __import__(module_name, level=0) @@ -347,7 +363,10 @@ self.fail("Non-optional module {0} raised an " "ImportError.".format(module_name)) else: - self.fail("DeprecationWarning not raised for {0}" + # For extension modules, check the __warningregistry__. + # They won't rerun their init code even with CleanImport. + if not check_deprecated_module(module_name): + self.fail("DeprecationWarning not raised for {0}" .format(module_name)) def test_platform_independent_removals(self): @@ -407,10 +426,8 @@ def test_main(): - with check_warnings(): - warnings.simplefilter("always") - run_unittest(TestPy3KWarnings, - TestStdlibRemovals) + run_unittest(TestPy3KWarnings, + TestStdlibRemovals) if __name__ == '__main__': test_main() Modified: python/branches/release26-maint/Lib/test/test_support.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_support.py (original) +++ python/branches/release26-maint/Lib/test/test_support.py Mon Aug 2 23:48:39 2010 @@ -437,10 +437,10 @@ if registry: registry.clear() with warnings.catch_warnings(record=True) as w: - # Disable filters, to record all warnings. Because - # test_warnings swap the module, we need to look up - # in the sys.modules dictionary. - sys.modules['warnings'].resetwarnings() + # Set filter "always" to record all warnings. Because + # test_warnings swap the module, we need to look up in + # the sys.modules dictionary. + sys.modules['warnings'].simplefilter("always") yield WarningsRecorder(w) # Filter the recorded warnings reraise = [warning.message for warning in w] From python-checkins at python.org Mon Aug 2 23:49:31 2010 From: python-checkins at python.org (georg.brandl) Date: Mon, 2 Aug 2010 23:49:31 +0200 (CEST) Subject: [Python-checkins] r83598 - python/branches/release27-maint Message-ID: <20100802214931.21E62EEC43@mail.python.org> Author: georg.brandl Date: Mon Aug 2 23:49:30 2010 New Revision: 83598 Log: Merged revisions 83551,83553 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83551 | georg.brandl | 2010-08-02 21:35:06 +0200 (Mo, 02 Aug 2010) | 1 line Remove XXX comment that was displayed. ........ r83553 | georg.brandl | 2010-08-02 21:39:17 +0200 (Mo, 02 Aug 2010) | 1 line Remove redundant information. ........ Modified: python/branches/release27-maint/ (props changed) From python-checkins at python.org Mon Aug 2 23:51:18 2010 From: python-checkins at python.org (georg.brandl) Date: Mon, 2 Aug 2010 23:51:18 +0200 (CEST) Subject: [Python-checkins] r83599 - python/branches/py3k/Doc/library/cgi.rst Message-ID: <20100802215118.4D594EEC20@mail.python.org> Author: georg.brandl Date: Mon Aug 2 23:51:18 2010 New Revision: 83599 Log: #9061: warn that single quotes are never escaped. Modified: python/branches/py3k/Doc/library/cgi.rst Modified: python/branches/py3k/Doc/library/cgi.rst ============================================================================== --- python/branches/py3k/Doc/library/cgi.rst (original) +++ python/branches/py3k/Doc/library/cgi.rst Mon Aug 2 23:51:18 2010 @@ -324,10 +324,13 @@ Convert the characters ``'&'``, ``'<'`` and ``'>'`` in string *s* to HTML-safe sequences. Use this if you need to display text that might contain such characters in HTML. If the optional flag *quote* is true, the quotation mark - character (``'"'``) is also translated; this helps for inclusion in an HTML - attribute value, as in ````. If the value to be quoted might - include single- or double-quote characters, or both, consider using the - :func:`quoteattr` function in the :mod:`xml.sax.saxutils` module instead. + character (``"``) is also translated; this helps for inclusion in an HTML + attribute value delimited by double quotes, as in ````. Note + that single quotes are never translated. + + If the value to be quoted might include single- or double-quote characters, + or both, consider using the :func:`quoteattr` function in the + :mod:`xml.sax.saxutils` module instead. .. _cgi-security: From python-checkins at python.org Mon Aug 2 23:51:51 2010 From: python-checkins at python.org (georg.brandl) Date: Mon, 2 Aug 2010 23:51:51 +0200 (CEST) Subject: [Python-checkins] r83600 - in python/branches/release27-maint: Doc/library/cgi.rst Message-ID: <20100802215151.9E360EEC20@mail.python.org> Author: georg.brandl Date: Mon Aug 2 23:51:51 2010 New Revision: 83600 Log: Merged revisions 83599 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83599 | georg.brandl | 2010-08-02 23:51:18 +0200 (Mo, 02 Aug 2010) | 1 line #9061: warn that single quotes are never escaped. ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Doc/library/cgi.rst Modified: python/branches/release27-maint/Doc/library/cgi.rst ============================================================================== --- python/branches/release27-maint/Doc/library/cgi.rst (original) +++ python/branches/release27-maint/Doc/library/cgi.rst Mon Aug 2 23:51:51 2010 @@ -349,10 +349,13 @@ Convert the characters ``'&'``, ``'<'`` and ``'>'`` in string *s* to HTML-safe sequences. Use this if you need to display text that might contain such characters in HTML. If the optional flag *quote* is true, the quotation mark - character (``'"'``) is also translated; this helps for inclusion in an HTML - attribute value, as in ````. If the value to be quoted might - include single- or double-quote characters, or both, consider using the - :func:`quoteattr` function in the :mod:`xml.sax.saxutils` module instead. + character (``"``) is also translated; this helps for inclusion in an HTML + attribute value delimited by double quotes, as in ````. Note + that single quotes are never translated. + + If the value to be quoted might include single- or double-quote characters, + or both, consider using the :func:`quoteattr` function in the + :mod:`xml.sax.saxutils` module instead. .. _cgi-security: From python-checkins at python.org Mon Aug 2 23:53:39 2010 From: python-checkins at python.org (ezio.melotti) Date: Mon, 2 Aug 2010 23:53:39 +0200 (CEST) Subject: [Python-checkins] r83601 - python/branches/release26-maint Message-ID: <20100802215339.B838BEEC07@mail.python.org> Author: ezio.melotti Date: Mon Aug 2 23:53:39 2010 New Revision: 83601 Log: Unblocked revisions 78814,79049 via svnmerge ........ r78814 | raymond.hettinger | 2010-03-09 13:29:10 +0200 (Tue, 09 Mar 2010) | 1 line Improve code clarity a bit. ........ r79049 | florent.xicluna | 2010-03-18 21:51:47 +0200 (Thu, 18 Mar 2010) | 2 lines #8155: Preserve backward compatibility for test_support.check_warnings(). Add regression tests. ........ Modified: python/branches/release26-maint/ (props changed) From python-checkins at python.org Mon Aug 2 23:55:33 2010 From: python-checkins at python.org (ezio.melotti) Date: Mon, 2 Aug 2010 23:55:33 +0200 (CEST) Subject: [Python-checkins] r83602 - python/branches/release26-maint Message-ID: <20100802215533.21B9EEEC42@mail.python.org> Author: ezio.melotti Date: Mon Aug 2 23:55:32 2010 New Revision: 83602 Log: Blocked revisions 78814 via svnmerge ........ r78814 | raymond.hettinger | 2010-03-09 13:29:10 +0200 (Tue, 09 Mar 2010) | 1 line Improve code clarity a bit. ........ Modified: python/branches/release26-maint/ (props changed) From python-checkins at python.org Mon Aug 2 23:56:44 2010 From: python-checkins at python.org (ezio.melotti) Date: Mon, 2 Aug 2010 23:56:44 +0200 (CEST) Subject: [Python-checkins] r83603 - python/branches/release26-maint Message-ID: <20100802215644.7FC28EEA4C@mail.python.org> Author: ezio.melotti Date: Mon Aug 2 23:56:44 2010 New Revision: 83603 Log: Unblocked revisions 78769 via svnmerge ........ r78769 | florent.xicluna | 2010-03-07 21:14:12 +0200 (Sun, 07 Mar 2010) | 2 lines Refresh the documentation for the test.test_support module. ........ Modified: python/branches/release26-maint/ (props changed) From python-checkins at python.org Tue Aug 3 00:01:34 2010 From: python-checkins at python.org (ezio.melotti) Date: Tue, 3 Aug 2010 00:01:34 +0200 (CEST) Subject: [Python-checkins] r83604 - in python/branches/release26-maint: Lib/test/test_support.py Lib/test/test_warnings.py Message-ID: <20100802220134.E5309EE9AD@mail.python.org> Author: ezio.melotti Date: Tue Aug 3 00:01:34 2010 New Revision: 83604 Log: Merged revisions 78769,79049 via svnmerge from svn+ssh://pythondev at svn.python.org/python/trunk ........ r78769 | florent.xicluna | 2010-03-07 21:14:12 +0200 (Sun, 07 Mar 2010) | 2 lines Refresh the documentation for the test.test_support module. ........ r79049 | florent.xicluna | 2010-03-18 21:51:47 +0200 (Thu, 18 Mar 2010) | 2 lines #8155: Preserve backward compatibility for test_support.check_warnings(). Add regression tests. ........ Modified: python/branches/release26-maint/ (props changed) python/branches/release26-maint/Lib/test/test_support.py python/branches/release26-maint/Lib/test/test_warnings.py Modified: python/branches/release26-maint/Lib/test/test_support.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_support.py (original) +++ python/branches/release26-maint/Lib/test/test_support.py Tue Aug 3 00:01:34 2010 @@ -457,11 +457,11 @@ if not seen and not quiet: # This filter caught nothing missing.append((msg, cat.__name__)) - for exc in reraise: - raise AssertionError("unhandled warning %r" % exc) - for filter in missing: - raise AssertionError("filter (%r, %s) did not caught any warning" % - filter) + if reraise: + raise AssertionError("unhandled warning %r" % reraise[0]) + if missing: + raise AssertionError("filter (%r, %s) did not catch any warning" % + missing[0]) @contextlib.contextmanager @@ -473,14 +473,19 @@ Optional argument: - if 'quiet' is True, it does not fail if a filter catches nothing - (default False) + (default True without argument, + default False if some filters are defined) Without argument, it defaults to: - check_warnings(("", Warning), quiet=False) + check_warnings(("", Warning), quiet=True) """ + quiet = kwargs.get('quiet') if not filters: filters = (("", Warning),) - return _filterwarnings(filters, kwargs.get('quiet')) + # Preserve backward compatibility + if quiet is None: + quiet = True + return _filterwarnings(filters, quiet) @contextlib.contextmanager Modified: python/branches/release26-maint/Lib/test/test_warnings.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_warnings.py (original) +++ python/branches/release26-maint/Lib/test/test_warnings.py Tue Aug 3 00:01:34 2010 @@ -600,19 +600,33 @@ def test_check_warnings(self): # Explicit tests for the test_support convenience wrapper wmod = self.module - if wmod is sys.modules['warnings']: - with test_support.check_warnings() as w: - self.assertEqual(w.warnings, []) - wmod.simplefilter("always") + if wmod is not sys.modules['warnings']: + return + with test_support.check_warnings(quiet=False) as w: + self.assertEqual(w.warnings, []) + wmod.simplefilter("always") + wmod.warn("foo") + self.assertEqual(str(w.message), "foo") + wmod.warn("bar") + self.assertEqual(str(w.message), "bar") + self.assertEqual(str(w.warnings[0].message), "foo") + self.assertEqual(str(w.warnings[1].message), "bar") + w.reset() + self.assertEqual(w.warnings, []) + + with test_support.check_warnings(): + # defaults to quiet=True without argument + pass + with test_support.check_warnings(('foo', UserWarning)): + wmod.warn("foo") + + with self.assertRaises(AssertionError): + with test_support.check_warnings(('', RuntimeWarning)): + # defaults to quiet=False with argument + pass + with self.assertRaises(AssertionError): + with test_support.check_warnings(('foo', RuntimeWarning)): wmod.warn("foo") - self.assertEqual(str(w.message), "foo") - wmod.warn("bar") - self.assertEqual(str(w.message), "bar") - self.assertEqual(str(w.warnings[0].message), "foo") - self.assertEqual(str(w.warnings[1].message), "bar") - w.reset() - self.assertEqual(w.warnings, []) - class CCatchWarningTests(CatchWarningTests): From python-checkins at python.org Tue Aug 3 00:08:58 2010 From: python-checkins at python.org (georg.brandl) Date: Tue, 3 Aug 2010 00:08:58 +0200 (CEST) Subject: [Python-checkins] r83605 - in python/branches/py3k/Tools/pynche: ChipViewer.py DetailsViewer.py ListViewer.py PyncheWidget.py StripViewer.py Switchboard.py TextViewer.py TypeinViewer.py Message-ID: <20100802220858.DDF45EEC0E@mail.python.org> Author: georg.brandl Date: Tue Aug 3 00:08:58 2010 New Revision: 83605 Log: Make the Pynche tool work with Python 3. Modified: python/branches/py3k/Tools/pynche/ChipViewer.py python/branches/py3k/Tools/pynche/DetailsViewer.py python/branches/py3k/Tools/pynche/ListViewer.py python/branches/py3k/Tools/pynche/PyncheWidget.py python/branches/py3k/Tools/pynche/StripViewer.py python/branches/py3k/Tools/pynche/Switchboard.py python/branches/py3k/Tools/pynche/TextViewer.py python/branches/py3k/Tools/pynche/TypeinViewer.py Modified: python/branches/py3k/Tools/pynche/ChipViewer.py ============================================================================== --- python/branches/py3k/Tools/pynche/ChipViewer.py (original) +++ python/branches/py3k/Tools/pynche/ChipViewer.py Tue Aug 3 00:08:58 2010 @@ -13,7 +13,7 @@ selected and nearest ChipWidgets. """ -from Tkinter import * +from tkinter import * import ColorDB Modified: python/branches/py3k/Tools/pynche/DetailsViewer.py ============================================================================== --- python/branches/py3k/Tools/pynche/DetailsViewer.py (original) +++ python/branches/py3k/Tools/pynche/DetailsViewer.py Tue Aug 3 00:08:58 2010 @@ -52,7 +52,7 @@ Shift + Right == +25 """ -from Tkinter import * +from tkinter import * STOP = 'Stop' WRAP = 'Wrap Around' Modified: python/branches/py3k/Tools/pynche/ListViewer.py ============================================================================== --- python/branches/py3k/Tools/pynche/ListViewer.py (original) +++ python/branches/py3k/Tools/pynche/ListViewer.py Tue Aug 3 00:08:58 2010 @@ -15,7 +15,7 @@ given name, without selecting the color. """ -from Tkinter import * +from tkinter import * import ColorDB ADDTOVIEW = 'Color %List Window...' Modified: python/branches/py3k/Tools/pynche/PyncheWidget.py ============================================================================== --- python/branches/py3k/Tools/pynche/PyncheWidget.py (original) +++ python/branches/py3k/Tools/pynche/PyncheWidget.py Tue Aug 3 00:08:58 2010 @@ -6,9 +6,8 @@ import sys import os -from Tkinter import * -import tkMessageBox -import tkFileDialog +from tkinter import * +from tkinter import messagebox, filedialog import ColorDB # Milliseconds between interrupt checks @@ -150,7 +149,7 @@ def __popup_about(self, event=None): from Main import __version__ - tkMessageBox.showinfo('About Pynche ' + __version__, + messagebox.showinfo('About Pynche ' + __version__, '''\ Pynche %s The PYthonically Natural @@ -168,7 +167,7 @@ def __load(self, event=None): while 1: idir, ifile = os.path.split(self.__sb.colordb().filename()) - file = tkFileDialog.askopenfilename( + file = filedialog.askopenfilename( filetypes=[('Text files', '*.txt'), ('All files', '*'), ], @@ -180,12 +179,12 @@ try: colordb = ColorDB.get_colordb(file) except IOError: - tkMessageBox.showerror('Read error', '''\ + messagebox.showerror('Read error', '''\ Could not open file for reading: %s''' % file) continue if colordb is None: - tkMessageBox.showerror('Unrecognized color file type', '''\ + messagebox.showerror('Unrecognized color file type', '''\ Unrecognized color file type in file: %s''' % file) continue @@ -249,6 +248,8 @@ +import functools + at functools.total_ordering class PopupViewer: def __init__(self, module, name, switchboard, root): self.__m = module @@ -279,8 +280,11 @@ self.__sb.add_view(self.__window) self.__window.deiconify() - def __cmp__(self, other): - return cmp(self.__menutext, other.__menutext) + def __eq__(self, other): + return self.__menutext == other.__menutext + + def __lt__(self, other): + return self.__menutext < other.__menutext def make_view_popups(switchboard, root, extrapath): Modified: python/branches/py3k/Tools/pynche/StripViewer.py ============================================================================== --- python/branches/py3k/Tools/pynche/StripViewer.py (original) +++ python/branches/py3k/Tools/pynche/StripViewer.py Tue Aug 3 00:08:58 2010 @@ -24,7 +24,7 @@ this can be slow. """ -from Tkinter import * +from tkinter import * import ColorDB # Load this script into the Tcl interpreter and call it in @@ -62,32 +62,32 @@ # red variations, green+blue = cyan constant def constant_red_generator(numchips, red, green, blue): seq = constant(numchips) - return list(map(None, [red] * numchips, seq, seq)) + return list(zip([red] * numchips, seq, seq)) # green variations, red+blue = magenta constant def constant_green_generator(numchips, red, green, blue): seq = constant(numchips) - return list(map(None, seq, [green] * numchips, seq)) + return list(zip(seq, [green] * numchips, seq)) # blue variations, red+green = yellow constant def constant_blue_generator(numchips, red, green, blue): seq = constant(numchips) - return list(map(None, seq, seq, [blue] * numchips)) + return list(zip(seq, seq, [blue] * numchips)) # red variations, green+blue = cyan constant def constant_cyan_generator(numchips, red, green, blue): seq = constant(numchips) - return list(map(None, seq, [green] * numchips, [blue] * numchips)) + return list(zip(seq, [green] * numchips, [blue] * numchips)) # green variations, red+blue = magenta constant def constant_magenta_generator(numchips, red, green, blue): seq = constant(numchips) - return list(map(None, [red] * numchips, seq, [blue] * numchips)) + return list(zip([red] * numchips, seq, [blue] * numchips)) # blue variations, red+green = yellow constant def constant_yellow_generator(numchips, red, green, blue): seq = constant(numchips) - return list(map(None, [red] * numchips, [green] * numchips, seq)) + return list(zip([red] * numchips, [green] * numchips, seq)) @@ -119,7 +119,7 @@ return arrow, text def _x(self): - coords = self._canvas.coords(self._TAG) + coords = list(self._canvas.coords(self._TAG)) assert coords return coords[0] @@ -151,7 +151,7 @@ return arrow, text def _x(self): - coords = self._canvas.coords(self._TAG) + coords = list(self._canvas.coords(self._TAG)) assert coords return coords[0] + self._ARROWWIDTH Modified: python/branches/py3k/Tools/pynche/Switchboard.py ============================================================================== --- python/branches/py3k/Tools/pynche/Switchboard.py (original) +++ python/branches/py3k/Tools/pynche/Switchboard.py Tue Aug 3 00:08:58 2010 @@ -42,7 +42,6 @@ """ import sys -from types import DictType import marshal @@ -62,10 +61,11 @@ if initfile: try: try: - fp = open(initfile) + fp = open(initfile, 'rb') self.__optiondb = marshal.load(fp) - if not isinstance(self.__optiondb, DictType): - print('Problem reading options from file:', initfile, file=sys.stderr) + if not isinstance(self.__optiondb, dict): + print('Problem reading options from file:', initfile, + file=sys.stderr) self.__optiondb = {} except (IOError, EOFError, ValueError): pass @@ -116,7 +116,7 @@ fp = None try: try: - fp = open(self.__initfile, 'w') + fp = open(self.__initfile, 'wb') except IOError: print('Cannot write options to file:', \ self.__initfile, file=sys.stderr) Modified: python/branches/py3k/Tools/pynche/TextViewer.py ============================================================================== --- python/branches/py3k/Tools/pynche/TextViewer.py (original) +++ python/branches/py3k/Tools/pynche/TextViewer.py Tue Aug 3 00:08:58 2010 @@ -15,7 +15,7 @@ in the text window (which only has a background). """ -from Tkinter import * +from tkinter import * import ColorDB ADDTOVIEW = 'Text Window...' Modified: python/branches/py3k/Tools/pynche/TypeinViewer.py ============================================================================== --- python/branches/py3k/Tools/pynche/TypeinViewer.py (original) +++ python/branches/py3k/Tools/pynche/TypeinViewer.py Tue Aug 3 00:08:58 2010 @@ -12,7 +12,7 @@ you must hit Return or Tab to select the color. """ -from Tkinter import * +from tkinter import * From python-checkins at python.org Tue Aug 3 00:25:16 2010 From: python-checkins at python.org (georg.brandl) Date: Tue, 3 Aug 2010 00:25:16 +0200 (CEST) Subject: [Python-checkins] r83606 - in python/branches/py3k/Tools/freeze: freeze.py makeconfig.py Message-ID: <20100802222516.AB0A0EEA74@mail.python.org> Author: georg.brandl Date: Tue Aug 3 00:25:16 2010 New Revision: 83606 Log: Minimum fixes to make freeze.py do something useful. Modified: python/branches/py3k/Tools/freeze/freeze.py python/branches/py3k/Tools/freeze/makeconfig.py Modified: python/branches/py3k/Tools/freeze/freeze.py ============================================================================== --- python/branches/py3k/Tools/freeze/freeze.py (original) +++ python/branches/py3k/Tools/freeze/freeze.py Tue Aug 3 00:25:16 2010 @@ -201,7 +201,7 @@ # modules that are imported by the Python runtime implicits = [] - for module in ('site', 'warnings',): + for module in ('site', 'warnings', 'encodings.utf_8', 'encodings.latin_1'): if module not in exclude: implicits.append(module) Modified: python/branches/py3k/Tools/freeze/makeconfig.py ============================================================================== --- python/branches/py3k/Tools/freeze/makeconfig.py (original) +++ python/branches/py3k/Tools/freeze/makeconfig.py Tue Aug 3 00:25:16 2010 @@ -3,14 +3,13 @@ # Write the config.c file -never = ['marshal', '__main__', 'builtins', 'sys', 'exceptions', '_warnings'] +never = ['marshal', 'imp', '_ast', '__main__', 'builtins', + 'sys', 'gc', '_warnings'] def makeconfig(infp, outfp, modules, with_ifdef=0): m1 = re.compile('-- ADDMODULE MARKER 1 --') m2 = re.compile('-- ADDMODULE MARKER 2 --') - while 1: - line = infp.readline() - if not line: break + for line in infp: outfp.write(line) if m1 and m1.search(line): m1 = None @@ -18,8 +17,8 @@ if mod in never: continue if with_ifdef: - outfp.write("#ifndef init%s\n"%mod) - outfp.write('extern void init%s(void);\n' % mod) + outfp.write("#ifndef PyInit_%s\n"%mod) + outfp.write('extern PyObject* PyInit_%s(void);\n' % mod) if with_ifdef: outfp.write("#endif\n") elif m2 and m2.search(line): @@ -27,7 +26,7 @@ for mod in modules: if mod in never: continue - outfp.write('\t{"%s", init%s},\n' % + outfp.write('\t{"%s", PyInit_%s},\n' % (mod, mod)) if m1: sys.stderr.write('MARKER 1 never found\n') From python-checkins at python.org Tue Aug 3 00:28:01 2010 From: python-checkins at python.org (georg.brandl) Date: Tue, 3 Aug 2010 00:28:01 +0200 (CEST) Subject: [Python-checkins] r83607 - python/branches/py3k/Tools/faqwiz Message-ID: <20100802222801.C3288EEC0E@mail.python.org> Author: georg.brandl Date: Tue Aug 3 00:28:01 2010 New Revision: 83607 Log: Remove faqwiz tool. Removed: python/branches/py3k/Tools/faqwiz/ From python-checkins at python.org Tue Aug 3 00:31:22 2010 From: python-checkins at python.org (georg.brandl) Date: Tue, 3 Aug 2010 00:31:22 +0200 (CEST) Subject: [Python-checkins] r83608 - python/branches/py3k/Tools/README Message-ID: <20100802223122.3AF9CEEB7E@mail.python.org> Author: georg.brandl Date: Tue Aug 3 00:31:22 2010 New Revision: 83608 Log: Update README for Tools. Modified: python/branches/py3k/Tools/README Modified: python/branches/py3k/Tools/README ============================================================================== --- python/branches/py3k/Tools/README (original) +++ python/branches/py3k/Tools/README Tue Aug 3 00:31:22 2010 @@ -1,31 +1,40 @@ This directory contains a number of Python programs that are useful while building or extending Python. -faqwiz FAQ Wizard: a CGI script for a user-editable FAQ. +buildbot Batchfiles for running on Windows buildslaves. -freeze Create a stand-alone executable from a Python program. +ccbench A Python concurrency benchmark. + +framer Generate boilerplate code for C extension types. + +freeze Create a stand-alone executable from a Python program. gdb Python code to be run inside gdb, to make it easier to debug Python itself (by David Malcolm). -i18n Tools for internationalization. pygettext.py - parses Python source code and generates .pot files, - and msgfmt.py generates a binary message catalog - from a catalog in text format. - -pynche A Tkinter-based color editor. - -scripts A number of useful single-file programs, e.g. tabnanny.py - (by Tim Peters), which checks for inconsistent mixing - of tabs and spaces. - -unicode Tools used to generate unicode database files for - Python 2.0 (by Fredrik Lundh). - -versioncheck A tool to automate checking whether you have the latest - version of a package (by Jack Jansen). - -world Script to take a list of Internet addresses and print - out where in the world those addresses originate from, - based on the top-level domain country code found in - the address. +i18n Tools for internationalization. pygettext.py + parses Python source code and generates .pot files, + and msgfmt.py generates a binary message catalog + from a catalog in text format. + +iobench Benchmark for the new Python I/O system. + +msi Support for packaging Python as an MSI package on Windows. + +pybench Comprehensive Python benchmarking suite. + +pynche A Tkinter-based color editor. + +scripts A number of useful single-file programs, e.g. tabnanny.py + (by Tim Peters), which checks for inconsistent mixing + of tabs and spaces. + +ssl Currently, a tool to fetch server certificates. + +unicode Tools used to generate unicode database files for + Python 2.0 (by Fredrik Lundh). + +world Script to take a list of Internet addresses and print + out where in the world those addresses originate from, + based on the top-level domain country code found in + the address. From python-checkins at python.org Tue Aug 3 00:53:22 2010 From: python-checkins at python.org (georg.brandl) Date: Tue, 3 Aug 2010 00:53:22 +0200 (CEST) Subject: [Python-checkins] r83609 - in python/branches/py3k/Tools/scripts: README byext.py checkappend.py classfix.py cvsfiles.py logmerge.py methfix.py setup.py xxci.py Message-ID: <20100802225322.B014CEEC88@mail.python.org> Author: georg.brandl Date: Tue Aug 3 00:53:22 2010 New Revision: 83609 Log: Update README, remove obsolete scripts. Removed: python/branches/py3k/Tools/scripts/checkappend.py python/branches/py3k/Tools/scripts/classfix.py python/branches/py3k/Tools/scripts/cvsfiles.py python/branches/py3k/Tools/scripts/logmerge.py python/branches/py3k/Tools/scripts/methfix.py python/branches/py3k/Tools/scripts/setup.py python/branches/py3k/Tools/scripts/xxci.py Modified: python/branches/py3k/Tools/scripts/README python/branches/py3k/Tools/scripts/byext.py Modified: python/branches/py3k/Tools/scripts/README ============================================================================== --- python/branches/py3k/Tools/scripts/README (original) +++ python/branches/py3k/Tools/scripts/README Tue Aug 3 00:53:22 2010 @@ -1,68 +1,65 @@ -This directory contains a collection of executable Python scripts that -are useful while building, extending or managing Python. Some (e.g., -dutree or lll) are also generally useful UNIX tools. +This directory contains a collection of executable Python scripts that are +useful while building, extending or managing Python. Some (e.g., dutree or lll) +are also generally useful UNIX tools. See also the Demo/scripts directory! -analyze_dxp.py Analyzes the result of sys.getdxp() -byext.py Print lines/words/chars stats of files by extension -byteyears.py Print product of a file's size and age -checkappend.py Search for multi-argument .append() calls -checkpyc.py Check presence and validity of ".pyc" files -classfix.py Convert old class syntax to new -cleanfuture.py Fix reduntant Python __future__ statements -combinerefs.py A helper for analyzing PYTHONDUMPREFS output. -copytime.py Copy one file's atime and mtime to another -crlf.py Change CRLF line endings to LF (Windows to Unix) -cvsfiles.py Print a list of files that are under CVS -db2pickle.py Dump a database file to a pickle -diff.py Print file diffs in context, unified, or ndiff formats -dutree.py Format du(1) output as a tree sorted by size -eptags.py Create Emacs TAGS file for Python modules +2to3 Main script for running the 2to3 conversion tool +analyze_dxp.py Analyzes the result of sys.getdxp() +byext.py Print lines/words/chars stats of files by extension +byteyears.py Print product of a file's size and age +checkpyc.py Check presence and validity of ".pyc" files +cleanfuture.py Fix redundant Python __future__ statements +combinerefs.py A helper for analyzing PYTHONDUMPREFS output +copytime.py Copy one file's atime and mtime to another +crlf.py Change CRLF line endings to LF (Windows to Unix) +db2pickle.py Dump a database file to a pickle +diff.py Print file diffs in context, unified, or ndiff formats +dutree.py Format du(1) output as a tree sorted by size +eptags.py Create Emacs TAGS file for Python modules find_recursionlimit.py Find the maximum recursion limit on this machine -finddiv.py A grep-like tool that looks for division operators -findlinksto.py Recursively find symbolic links to a given path prefix -findnocoding.py Find source files which need an encoding declaration -fixcid.py Massive identifier substitution on C source files -fixdiv.py Tool to fix division operators. -fixheader.py Add some cpp magic to a C include file -fixnotice.py Fix the copyright notice in source files -fixps.py Fix Python scripts' first line (if #!) -ftpmirror.py FTP mirror script -google.py Open a webbrowser with Google -gprof2html.py Transform gprof(1) output into useful HTML -h2py.py Translate #define's into Python assignments -idle Main program to start IDLE -ifdef.py Remove #if(n)def groups from C sources -lfcr.py Change LF line endings to CRLF (Unix to Windows) -linktree.py Make a copy of a tree with links to original files -lll.py Find and list symbolic links in current directory -logmerge.py Consolidate CVS/RCS logs read from stdin -mailerdaemon.py parse error messages from mailer daemons (Sjoerd&Jack) -md5sum.py Print MD5 checksums of argument files. -methfix.py Fix old method syntax def f(self, (a1, ..., aN)): -mkreal.py Turn a symbolic link into a real file or directory -ndiff.py Intelligent diff between text files (Tim Peters) -nm2def.py Create a template for PC/python_nt.def (Marc Lemburg) -objgraph.py Print object graph from nm output on a library -parseentities.py Utility for parsing HTML entity definitions -pathfix.py Change #!/usr/local/bin/python into something else -pdeps.py Print dependencies between Python modules -pickle2db.py Load a pickle generated by db2pickle.py to a database -pindent.py Indent Python code, giving block-closing comments -ptags.py Create vi tags file for Python modules -pydoc Python documentation browser. -pysource.py Find Python source files -redemo.py Basic regular expression demonstration facility -reindent.py Change .py files to use 4-space indents. -rgrep.py Reverse grep through a file (useful for big logfiles) -serve.py Small wsgiref-based web server, used in make serve in Doc -setup.py Install all scripts listed here -suff.py Sort a list of files by suffix -svneol.py Sets svn:eol-style on all files in directory -texcheck.py Validate Python LaTeX formatting (Raymond Hettinger) -texi2html.py Convert GNU texinfo files into HTML -treesync.py Synchronize source trees (very ideosyncratic) -untabify.py Replace tabs with spaces in argument files -which.py Find a program in $PATH -xxci.py Wrapper for rcsdiff and ci +finddiv.py A grep-like tool that looks for division operators +findlinksto.py Recursively find symbolic links to a given path prefix +findnocoding.py Find source files which need an encoding declaration +fixcid.py Massive identifier substitution on C source files +fixdiv.py Tool to fix division operators. +fixheader.py Add some cpp magic to a C include file +fixnotice.py Fix the copyright notice in source files +fixps.py Fix Python scripts' first line (if #!) +ftpmirror.py FTP mirror script +google.py Open a webbrowser with Google +gprof2html.py Transform gprof(1) output into useful HTML +h2py.py Translate #define's into Python assignments +idle3 Main program to start IDLE +ifdef.py Remove #if(n)def groups from C sources +lfcr.py Change LF line endings to CRLF (Unix to Windows) +linktree.py Make a copy of a tree with links to original files +lll.py Find and list symbolic links in current directory +mailerdaemon.py Parse error messages from mailer daemons (Sjoerd&Jack) +make_ctype.py Generate ctype.h replacement in stringobject.c +md5sum.py Print MD5 checksums of argument files +mkreal.py Turn a symbolic link into a real file or directory +ndiff.py Intelligent diff between text files (Tim Peters) +nm2def.py Create a template for PC/python_nt.def (Marc Lemburg) +objgraph.py Print object graph from nm output on a library +parseentities.py Utility for parsing HTML entity definitions +patchcheck.py Perform common checks and cleanup before committing +pathfix.py Change #!/usr/local/bin/python into something else +pdeps.py Print dependencies between Python modules +pickle2db.py Load a pickle generated by db2pickle.py to a database +pindent.py Indent Python code, giving block-closing comments +ptags.py Create vi tags file for Python modules +pydoc3 Python documentation browser +pysource.py Find Python source files +redemo.py Basic regular expression demonstration facility +reindent.py Change .py files to use 4-space indents +reindent-rst.py Fix-up reStructuredText file whitespace +rgrep.py Reverse grep through a file (useful for big logfiles) +serve.py Small wsgiref-based web server, used in make serve in Doc +suff.py Sort a list of files by suffix +svneol.py Set svn:eol-style on all files in directory +texi2html.py Convert GNU texinfo files into HTML +treesync.py Synchronize source trees (very idiosyncratic) +untabify.py Replace tabs with spaces in argument files +win_add2path.py Add Python to the search path on Windows +which.py Find a program in $PATH Modified: python/branches/py3k/Tools/scripts/byext.py ============================================================================== --- python/branches/py3k/Tools/scripts/byext.py (original) +++ python/branches/py3k/Tools/scripts/byext.py Tue Aug 3 00:53:22 2010 @@ -1,4 +1,4 @@ -#! /usr/bin/env python3.0 +#! /usr/bin/env python3 """Show file statistics by extension.""" Deleted: python/branches/py3k/Tools/scripts/checkappend.py ============================================================================== --- python/branches/py3k/Tools/scripts/checkappend.py Tue Aug 3 00:53:22 2010 +++ (empty file) @@ -1,169 +0,0 @@ -#! /usr/bin/env python3 - -# Released to the public domain, by Tim Peters, 28 February 2000. - -"""checkappend.py -- search for multi-argument .append() calls. - -Usage: specify one or more file or directory paths: - checkappend [-v] file_or_dir [file_or_dir] ... - -Each file_or_dir is checked for multi-argument .append() calls. When -a directory, all .py files in the directory, and recursively in its -subdirectories, are checked. - -Use -v for status msgs. Use -vv for more status msgs. - -In the absence of -v, the only output is pairs of the form - - filename(linenumber): - line containing the suspicious append - -Note that this finds multi-argument append calls regardless of whether -they're attached to list objects. If a module defines a class with an -append method that takes more than one argument, calls to that method -will be listed. - -Note that this will not find multi-argument list.append calls made via a -bound method object. For example, this is not caught: - - somelist = [] - push = somelist.append - push(1, 2, 3) -""" - -__version__ = 1, 0, 0 - -import os -import sys -import getopt -import tokenize - -verbose = 0 - -def errprint(*args): - msg = ' '.join(args) - sys.stderr.write(msg) - sys.stderr.write("\n") - -def main(): - args = sys.argv[1:] - global verbose - try: - opts, args = getopt.getopt(sys.argv[1:], "v") - except getopt.error as msg: - errprint(str(msg) + "\n\n" + __doc__) - return - for opt, optarg in opts: - if opt == '-v': - verbose = verbose + 1 - if not args: - errprint(__doc__) - return - for arg in args: - check(arg) - -def check(file): - if os.path.isdir(file) and not os.path.islink(file): - if verbose: - print("%r: listing directory" % (file,)) - names = os.listdir(file) - for name in names: - fullname = os.path.join(file, name) - if ((os.path.isdir(fullname) and - not os.path.islink(fullname)) - or os.path.normcase(name[-3:]) == ".py"): - check(fullname) - return - - try: - f = open(file) - except IOError as msg: - errprint("%r: I/O Error: %s" % (file, msg)) - return - - if verbose > 1: - print("checking %r ..." % (file,)) - - ok = AppendChecker(file, f).run() - if verbose and ok: - print("%r: Clean bill of health." % (file,)) - -[FIND_DOT, - FIND_APPEND, - FIND_LPAREN, - FIND_COMMA, - FIND_STMT] = range(5) - -class AppendChecker: - def __init__(self, fname, file): - self.fname = fname - self.file = file - self.state = FIND_DOT - self.nerrors = 0 - - def run(self): - try: - tokens = tokenize.generate_tokens(self.file.readline) - for _token in tokens: - self.tokeneater(*_token) - except tokenize.TokenError as msg: - errprint("%r: Token Error: %s" % (self.fname, msg)) - self.nerrors = self.nerrors + 1 - return self.nerrors == 0 - - def tokeneater(self, type, token, start, end, line, - NEWLINE=tokenize.NEWLINE, - JUNK=(tokenize.COMMENT, tokenize.NL), - OP=tokenize.OP, - NAME=tokenize.NAME): - - state = self.state - - if type in JUNK: - pass - - elif state is FIND_DOT: - if type is OP and token == ".": - state = FIND_APPEND - - elif state is FIND_APPEND: - if type is NAME and token == "append": - self.line = line - self.lineno = start[0] - state = FIND_LPAREN - else: - state = FIND_DOT - - elif state is FIND_LPAREN: - if type is OP and token == "(": - self.level = 1 - state = FIND_COMMA - else: - state = FIND_DOT - - elif state is FIND_COMMA: - if type is OP: - if token in ("(", "{", "["): - self.level = self.level + 1 - elif token in (")", "}", "]"): - self.level = self.level - 1 - if self.level == 0: - state = FIND_DOT - elif token == "," and self.level == 1: - self.nerrors = self.nerrors + 1 - print("%s(%d):\n%s" % (self.fname, self.lineno, - self.line)) - # don't gripe about this stmt again - state = FIND_STMT - - elif state is FIND_STMT: - if type is NEWLINE: - state = FIND_DOT - - else: - raise SystemError("unknown internal state '%r'" % (state,)) - - self.state = state - -if __name__ == '__main__': - main() Deleted: python/branches/py3k/Tools/scripts/classfix.py ============================================================================== --- python/branches/py3k/Tools/scripts/classfix.py Tue Aug 3 00:53:22 2010 +++ (empty file) @@ -1,190 +0,0 @@ -#! /usr/bin/env python3 - -# This script is obsolete -- it is kept for historical purposes only. -# -# Fix Python source files to use the new class definition syntax, i.e., -# the syntax used in Python versions before 0.9.8: -# class C() = base(), base(), ...: ... -# is changed to the current syntax: -# class C(base, base, ...): ... -# -# The script uses heuristics to find class definitions that usually -# work but occasionally can fail; carefully check the output! -# -# Command line arguments are files or directories to be processed. -# Directories are searched recursively for files whose name looks -# like a python module. -# Symbolic links are always ignored (except as explicit directory -# arguments). Of course, the original file is kept as a back-up -# (with a "~" attached to its name). -# -# Changes made are reported to stdout in a diff-like format. -# -# Undoubtedly you can do this using find and sed or perl, but this is -# a nice example of Python code that recurses down a directory tree -# and uses regular expressions. Also note several subtleties like -# preserving the file's mode and avoiding to even write a temp file -# when no changes are needed for a file. -# -# NB: by changing only the function fixline() you can turn this -# into a program for a different change to Python programs... - -import sys -import re -import os -from stat import * - -err = sys.stderr.write -dbg = err -rep = sys.stdout.write - -def main(): - bad = 0 - if not sys.argv[1:]: # No arguments - err('usage: ' + sys.argv[0] + ' file-or-directory ...\n') - sys.exit(2) - for arg in sys.argv[1:]: - if os.path.isdir(arg): - if recursedown(arg): bad = 1 - elif os.path.islink(arg): - err(arg + ': will not process symbolic links\n') - bad = 1 - else: - if fix(arg): bad = 1 - sys.exit(bad) - -ispythonprog = re.compile('^[a-zA-Z0-9_]+\.py$') -def ispython(name): - return ispythonprog.match(name) >= 0 - -def recursedown(dirname): - dbg('recursedown(%r)\n' % (dirname,)) - bad = 0 - try: - names = os.listdir(dirname) - except os.error as msg: - err('%s: cannot list directory: %r\n' % (dirname, msg)) - return 1 - names.sort() - subdirs = [] - for name in names: - if name in (os.curdir, os.pardir): continue - fullname = os.path.join(dirname, name) - if os.path.islink(fullname): pass - elif os.path.isdir(fullname): - subdirs.append(fullname) - elif ispython(name): - if fix(fullname): bad = 1 - for fullname in subdirs: - if recursedown(fullname): bad = 1 - return bad - -def fix(filename): -## dbg('fix(%r)\n' % (filename,)) - try: - f = open(filename, 'r') - except IOError as msg: - err('%s: cannot open: %r\n' % (filename, msg)) - return 1 - head, tail = os.path.split(filename) - tempname = os.path.join(head, '@' + tail) - g = None - # If we find a match, we rewind the file and start over but - # now copy everything to a temp file. - lineno = 0 - while 1: - line = f.readline() - if not line: break - lineno = lineno + 1 - while line[-2:] == '\\\n': - nextline = f.readline() - if not nextline: break - line = line + nextline - lineno = lineno + 1 - newline = fixline(line) - if newline != line: - if g is None: - try: - g = open(tempname, 'w') - except IOError as msg: - f.close() - err('%s: cannot create: %r\n' % (tempname, msg)) - return 1 - f.seek(0) - lineno = 0 - rep(filename + ':\n') - continue # restart from the beginning - rep(repr(lineno) + '\n') - rep('< ' + line) - rep('> ' + newline) - if g is not None: - g.write(newline) - - # End of file - f.close() - if not g: return 0 # No changes - - # Finishing touch -- move files - - # First copy the file's mode to the temp file - try: - statbuf = os.stat(filename) - os.chmod(tempname, statbuf[ST_MODE] & 0o7777) - except os.error as msg: - err('%s: warning: chmod failed (%r)\n' % (tempname, msg)) - # Then make a backup of the original file as filename~ - try: - os.rename(filename, filename + '~') - except os.error as msg: - err('%s: warning: backup failed (%r)\n' % (filename, msg)) - # Now move the temp file to the original file - try: - os.rename(tempname, filename) - except os.error as msg: - err('%s: rename failed (%r)\n' % (filename, msg)) - return 1 - # Return succes - return 0 - -# This expression doesn't catch *all* class definition headers, -# but it's pretty darn close. -classexpr = '^([ \t]*class +[a-zA-Z0-9_]+) *( *) *((=.*)?):' -classprog = re.compile(classexpr) - -# Expressions for finding base class expressions. -baseexpr = '^ *(.*) *( *) *$' -baseprog = re.compile(baseexpr) - -def fixline(line): - if classprog.match(line) < 0: # No 'class' keyword -- no change - return line - - (a0, b0), (a1, b1), (a2, b2) = classprog.regs[:3] - # a0, b0 = Whole match (up to ':') - # a1, b1 = First subexpression (up to classname) - # a2, b2 = Second subexpression (=.*) - head = line[:b1] - tail = line[b0:] # Unmatched rest of line - - if a2 == b2: # No base classes -- easy case - return head + ':' + tail - - # Get rid of leading '=' - basepart = line[a2+1:b2] - - # Extract list of base expressions - bases = basepart.split(',') - - # Strip trailing '()' from each base expression - for i in range(len(bases)): - if baseprog.match(bases[i]) >= 0: - x1, y1 = baseprog.regs[1] - bases[i] = bases[i][x1:y1] - - # Join the bases back again and build the new line - basepart = ', '.join(bases) - - return head + '(' + basepart + '):' + tail - -if __name__ == '__main__': - main() Deleted: python/branches/py3k/Tools/scripts/cvsfiles.py ============================================================================== --- python/branches/py3k/Tools/scripts/cvsfiles.py Tue Aug 3 00:53:22 2010 +++ (empty file) @@ -1,72 +0,0 @@ -#! /usr/bin/env python3 - -"""Print a list of files that are mentioned in CVS directories. - -Usage: cvsfiles.py [-n file] [directory] ... - -If the '-n file' option is given, only files under CVS that are newer -than the given file are printed; by default, all files under CVS are -printed. As a special case, if a file does not exist, it is always -printed. -""" - -import os -import sys -import stat -import getopt - -cutofftime = 0 - -def main(): - try: - opts, args = getopt.getopt(sys.argv[1:], "n:") - except getopt.error as msg: - print(msg) - print(__doc__, end=' ') - return 1 - global cutofftime - newerfile = None - for o, a in opts: - if o == '-n': - cutofftime = getmtime(a) - if args: - for arg in args: - process(arg) - else: - process(".") - -def process(dir): - cvsdir = 0 - subdirs = [] - names = os.listdir(dir) - for name in names: - fullname = os.path.join(dir, name) - if name == "CVS": - cvsdir = fullname - else: - if os.path.isdir(fullname): - if not os.path.islink(fullname): - subdirs.append(fullname) - if cvsdir: - entries = os.path.join(cvsdir, "Entries") - for e in open(entries).readlines(): - words = e.split('/') - if words[0] == '' and words[1:]: - name = words[1] - fullname = os.path.join(dir, name) - if cutofftime and getmtime(fullname) <= cutofftime: - pass - else: - print(fullname) - for sub in subdirs: - process(sub) - -def getmtime(filename): - try: - st = os.stat(filename) - except os.error: - return 0 - return st[stat.ST_MTIME] - -if __name__ == '__main__': - main() Deleted: python/branches/py3k/Tools/scripts/logmerge.py ============================================================================== --- python/branches/py3k/Tools/scripts/logmerge.py Tue Aug 3 00:53:22 2010 +++ (empty file) @@ -1,185 +0,0 @@ -#! /usr/bin/env python3 - -"""Consolidate a bunch of CVS or RCS logs read from stdin. - -Input should be the output of a CVS or RCS logging command, e.g. - - cvs log -rrelease14: - -which dumps all log messages from release1.4 upwards (assuming that -release 1.4 was tagged with tag 'release14'). Note the trailing -colon! - -This collects all the revision records and outputs them sorted by date -rather than by file, collapsing duplicate revision record, i.e., -records with the same message for different files. - -The -t option causes it to truncate (discard) the last revision log -entry; this is useful when using something like the above cvs log -command, which shows the revisions including the given tag, while you -probably want everything *since* that tag. - -The -r option reverses the output (oldest first; the default is oldest -last). - -The -b tag option restricts the output to *only* checkin messages -belonging to the given branch tag. The form -b HEAD restricts the -output to checkin messages belonging to the CVS head (trunk). (It -produces some output if tag is a non-branch tag, but this output is -not very useful.) - --h prints this message and exits. - -XXX This code was created by reverse engineering CVS 1.9 and RCS 5.7 -from their output. -""" - -import sys, errno, getopt, re - -sep1 = '='*77 + '\n' # file separator -sep2 = '-'*28 + '\n' # revision separator - -def main(): - """Main program""" - truncate_last = 0 - reverse = 0 - branch = None - opts, args = getopt.getopt(sys.argv[1:], "trb:h") - for o, a in opts: - if o == '-t': - truncate_last = 1 - elif o == '-r': - reverse = 1 - elif o == '-b': - branch = a - elif o == '-h': - print(__doc__) - sys.exit(0) - database = [] - while 1: - chunk = read_chunk(sys.stdin) - if not chunk: - break - records = digest_chunk(chunk, branch) - if truncate_last: - del records[-1] - database[len(database):] = records - database.sort() - if not reverse: - database.reverse() - format_output(database) - -def read_chunk(fp): - """Read a chunk -- data for one file, ending with sep1. - - Split the chunk in parts separated by sep2. - - """ - chunk = [] - lines = [] - while 1: - line = fp.readline() - if not line: - break - if line == sep1: - if lines: - chunk.append(lines) - break - if line == sep2: - if lines: - chunk.append(lines) - lines = [] - else: - lines.append(line) - return chunk - -def digest_chunk(chunk, branch=None): - """Digest a chunk -- extract working file name and revisions""" - lines = chunk[0] - key = 'Working file:' - keylen = len(key) - for line in lines: - if line[:keylen] == key: - working_file = line[keylen:].strip() - break - else: - working_file = None - if branch is None: - pass - elif branch == "HEAD": - branch = re.compile(r"^\d+\.\d+$") - else: - revisions = {} - key = 'symbolic names:\n' - found = 0 - for line in lines: - if line == key: - found = 1 - elif found: - if line[0] in '\t ': - tag, rev = line.split() - if tag[-1] == ':': - tag = tag[:-1] - revisions[tag] = rev - else: - found = 0 - rev = revisions.get(branch) - branch = re.compile(r"^<>$") # <> to force a mismatch by default - if rev: - if rev.find('.0.') >= 0: - rev = rev.replace('.0.', '.') - branch = re.compile(r"^" + re.escape(rev) + r"\.\d+$") - records = [] - for lines in chunk[1:]: - revline = lines[0] - dateline = lines[1] - text = lines[2:] - words = dateline.split() - author = None - if len(words) >= 3 and words[0] == 'date:': - dateword = words[1] - timeword = words[2] - if timeword[-1:] == ';': - timeword = timeword[:-1] - date = dateword + ' ' + timeword - if len(words) >= 5 and words[3] == 'author:': - author = words[4] - if author[-1:] == ';': - author = author[:-1] - else: - date = None - text.insert(0, revline) - words = revline.split() - if len(words) >= 2 and words[0] == 'revision': - rev = words[1] - else: - # No 'revision' line -- weird... - rev = None - text.insert(0, revline) - if branch: - if rev is None or not branch.match(rev): - continue - records.append((date, working_file, rev, author, text)) - return records - -def format_output(database): - prevtext = None - prev = [] - database.append((None, None, None, None, None)) # Sentinel - for (date, working_file, rev, author, text) in database: - if text != prevtext: - if prev: - print(sep2, end=' ') - for (p_date, p_working_file, p_rev, p_author) in prev: - print(p_date, p_author, p_working_file, p_rev) - sys.stdout.writelines(prevtext) - prev = [] - prev.append((date, working_file, rev, author)) - prevtext = text - -if __name__ == '__main__': - try: - main() - except IOError as e: - if e.errno != errno.EPIPE: - raise Deleted: python/branches/py3k/Tools/scripts/methfix.py ============================================================================== --- python/branches/py3k/Tools/scripts/methfix.py Tue Aug 3 00:53:22 2010 +++ (empty file) @@ -1,171 +0,0 @@ -#! /usr/bin/env python3 - -# Fix Python source files to avoid using -# def method(self, (arg1, ..., argn)): -# instead of the more rational -# def method(self, arg1, ..., argn): -# -# Command line arguments are files or directories to be processed. -# Directories are searched recursively for files whose name looks -# like a python module. -# Symbolic links are always ignored (except as explicit directory -# arguments). Of course, the original file is kept as a back-up -# (with a "~" attached to its name). -# It complains about binaries (files containing null bytes) -# and about files that are ostensibly not Python files: if the first -# line starts with '#!' and does not contain the string 'python'. -# -# Changes made are reported to stdout in a diff-like format. -# -# Undoubtedly you can do this using find and sed or perl, but this is -# a nice example of Python code that recurses down a directory tree -# and uses regular expressions. Also note several subtleties like -# preserving the file's mode and avoiding to even write a temp file -# when no changes are needed for a file. -# -# NB: by changing only the function fixline() you can turn this -# into a program for a different change to Python programs... - -import sys -import re -import os -from stat import * - -err = sys.stderr.write -dbg = err -rep = sys.stdout.write - -def main(): - bad = 0 - if not sys.argv[1:]: # No arguments - err('usage: ' + sys.argv[0] + ' file-or-directory ...\n') - sys.exit(2) - for arg in sys.argv[1:]: - if os.path.isdir(arg): - if recursedown(arg): bad = 1 - elif os.path.islink(arg): - err(arg + ': will not process symbolic links\n') - bad = 1 - else: - if fix(arg): bad = 1 - sys.exit(bad) - -ispythonprog = re.compile('^[a-zA-Z0-9_]+\.py$') -def ispython(name): - return ispythonprog.match(name) >= 0 - -def recursedown(dirname): - dbg('recursedown(%r)\n' % (dirname,)) - bad = 0 - try: - names = os.listdir(dirname) - except os.error as msg: - err('%s: cannot list directory: %r\n' % (dirname, msg)) - return 1 - names.sort() - subdirs = [] - for name in names: - if name in (os.curdir, os.pardir): continue - fullname = os.path.join(dirname, name) - if os.path.islink(fullname): pass - elif os.path.isdir(fullname): - subdirs.append(fullname) - elif ispython(name): - if fix(fullname): bad = 1 - for fullname in subdirs: - if recursedown(fullname): bad = 1 - return bad - -def fix(filename): -## dbg('fix(%r)\n' % (filename,)) - try: - f = open(filename, 'r') - except IOError as msg: - err('%s: cannot open: %r\n' % (filename, msg)) - return 1 - head, tail = os.path.split(filename) - tempname = os.path.join(head, '@' + tail) - g = None - # If we find a match, we rewind the file and start over but - # now copy everything to a temp file. - lineno = 0 - while 1: - line = f.readline() - if not line: break - lineno = lineno + 1 - if g is None and '\0' in line: - # Check for binary files - err(filename + ': contains null bytes; not fixed\n') - f.close() - return 1 - if lineno == 1 and g is None and line[:2] == '#!': - # Check for non-Python scripts - words = line[2:].split() - if words and re.search('[pP]ython', words[0]) < 0: - msg = filename + ': ' + words[0] - msg = msg + ' script; not fixed\n' - err(msg) - f.close() - return 1 - while line[-2:] == '\\\n': - nextline = f.readline() - if not nextline: break - line = line + nextline - lineno = lineno + 1 - newline = fixline(line) - if newline != line: - if g is None: - try: - g = open(tempname, 'w') - except IOError as msg: - f.close() - err('%s: cannot create: %r\n' % (tempname, msg)) - return 1 - f.seek(0) - lineno = 0 - rep(filename + ':\n') - continue # restart from the beginning - rep(repr(lineno) + '\n') - rep('< ' + line) - rep('> ' + newline) - if g is not None: - g.write(newline) - - # End of file - f.close() - if not g: return 0 # No changes - - # Finishing touch -- move files - - # First copy the file's mode to the temp file - try: - statbuf = os.stat(filename) - os.chmod(tempname, statbuf[ST_MODE] & 0o7777) - except os.error as msg: - err('%s: warning: chmod failed (%r)\n' % (tempname, msg)) - # Then make a backup of the original file as filename~ - try: - os.rename(filename, filename + '~') - except os.error as msg: - err('%s: warning: backup failed (%r)\n' % (filename, msg)) - # Now move the temp file to the original file - try: - os.rename(tempname, filename) - except os.error as msg: - err('%s: rename failed (%r)\n' % (filename, msg)) - return 1 - # Return succes - return 0 - - -fixpat = '^[ \t]+def +[a-zA-Z0-9_]+ *( *self *, *(( *(.*) *)) *) *:' -fixprog = re.compile(fixpat) - -def fixline(line): - if fixprog.match(line) >= 0: - (a, b), (c, d) = fixprog.regs[1:3] - line = line[:a] + line[c:d] + line[b:] - return line - -if __name__ == '__main__': - main() Deleted: python/branches/py3k/Tools/scripts/setup.py ============================================================================== --- python/branches/py3k/Tools/scripts/setup.py Tue Aug 3 00:53:22 2010 +++ (empty file) @@ -1,20 +0,0 @@ -from distutils.core import setup - -if __name__ == '__main__': - setup( - scripts=[ - 'byteyears.py', - 'checkpyc.py', - 'copytime.py', - 'crlf.py', - 'dutree.py', - 'ftpmirror.py', - 'h2py.py', - 'lfcr.py', - '../i18n/pygettext.py', - 'logmerge.py', - '../../Lib/tabnanny.py', - '../../Lib/timeit.py', - 'untabify.py', - ], - ) Deleted: python/branches/py3k/Tools/scripts/xxci.py ============================================================================== --- python/branches/py3k/Tools/scripts/xxci.py Tue Aug 3 00:53:22 2010 +++ (empty file) @@ -1,121 +0,0 @@ -#! /usr/bin/env python3 - -# xxci -# -# check in files for which rcsdiff returns nonzero exit status - -import sys -import os -from stat import * -import fnmatch - -EXECMAGIC = '\001\140\000\010' - -MAXSIZE = 200*1024 # Files this big must be binaries and are skipped. - -def getargs(): - args = sys.argv[1:] - if args: - return args - print('No arguments, checking almost *, in "ls -t" order') - list = [] - for file in os.listdir(os.curdir): - if not skipfile(file): - list.append((getmtime(file), file)) - list.sort() - if not list: - print('Nothing to do -- exit 1') - sys.exit(1) - list.sort() - list.reverse() - for mtime, file in list: args.append(file) - return args - -def getmtime(file): - try: - st = os.stat(file) - return st[ST_MTIME] - except os.error: - return -1 - -badnames = ['tags', 'TAGS', 'xyzzy', 'nohup.out', 'core'] -badprefixes = ['.', ',', '@', '#', 'o.'] -badsuffixes = \ - ['~', '.a', '.o', '.old', '.bak', '.orig', '.new', '.prev', '.not', \ - '.pyc', '.fdc', '.rgb', '.elc', ',v'] -ignore = [] - -def setup(): - ignore[:] = badnames - for p in badprefixes: - ignore.append(p + '*') - for p in badsuffixes: - ignore.append('*' + p) - try: - f = open('.xxcign', 'r') - except IOError: - return - ignore[:] = ignore + f.read().split() - -def skipfile(file): - for p in ignore: - if fnmatch.fnmatch(file, p): return 1 - try: - st = os.lstat(file) - except os.error: - return 1 # Doesn't exist -- skip it - # Skip non-plain files. - if not S_ISREG(st[ST_MODE]): return 1 - # Skip huge files -- probably binaries. - if st[ST_SIZE] >= MAXSIZE: return 1 - # Skip executables - try: - data = open(file, 'r').read(len(EXECMAGIC)) - if data == EXECMAGIC: return 1 - except: - pass - return 0 - -def badprefix(file): - for bad in badprefixes: - if file[:len(bad)] == bad: return 1 - return 0 - -def badsuffix(file): - for bad in badsuffixes: - if file[-len(bad):] == bad: return 1 - return 0 - -def go(args): - for file in args: - print(file + ':') - if differing(file): - showdiffs(file) - if askyesno('Check in ' + file + ' ? '): - sts = os.system('rcs -l ' + file) # ignored - sts = os.system('ci -l ' + file) - -def differing(file): - cmd = 'co -p ' + file + ' 2>/dev/null | cmp -s - ' + file - sts = os.system(cmd) - return sts != 0 - -def showdiffs(file): - cmd = 'rcsdiff ' + file + ' 2>&1 | ${PAGER-more}' - sts = os.system(cmd) - -def raw_input(prompt): - sys.stdout.write(prompt) - sys.stdout.flush() - return sys.stdin.readline() - -def askyesno(prompt): - s = input(prompt) - return s in ['y', 'yes'] - -if __name__ == '__main__': - try: - setup() - go(getargs()) - except KeyboardInterrupt: - print('[Intr]') From python-checkins at python.org Tue Aug 3 00:55:17 2010 From: python-checkins at python.org (georg.brandl) Date: Tue, 3 Aug 2010 00:55:17 +0200 (CEST) Subject: [Python-checkins] r83610 - in python/branches/py3k/Demo/scripts: README toaiff.py Message-ID: <20100802225517.3CD26EEC87@mail.python.org> Author: georg.brandl Date: Tue Aug 3 00:55:17 2010 New Revision: 83610 Log: Update README, remove obsolete script. Removed: python/branches/py3k/Demo/scripts/toaiff.py Modified: python/branches/py3k/Demo/scripts/README Modified: python/branches/py3k/Demo/scripts/README ============================================================================== --- python/branches/py3k/Demo/scripts/README (original) +++ python/branches/py3k/Demo/scripts/README Tue Aug 3 00:55:17 2010 @@ -2,21 +2,21 @@ See also the Tools/scripts directory! -beer.py Print the classic 'bottles of beer' list -eqfix.py Fix .py files to use the correct equality test operator -fact.py Factorize numbers -find-uname.py Search for Unicode characters using regexps -from.py Summarize mailbox -lpwatch.py Watch BSD line printer queues -makedir.py Like mkdir -p -markov.py Markov chain simulation of words or characters -mboxconvert.py Convert MH or MMDF mailboxes to unix mailbox format -morse.py Produce morse code (as an AIFF file) -newslist.py List all newsgroups on a NNTP server as HTML pages -pi.py Print all digits of pi -- given enough time and memory -pp.py Emulate some Perl command line options -primes.py Print prime numbers -queens.py Dijkstra's solution to Wirth's "N Queens problem" -script.py Equivalent to BSD script(1) -- by Steen Lumholt -unbirthday.py Print unbirthday count -update.py Update a bunch of files according to a script. +beer.py Print the classic 'bottles of beer' list +eqfix.py Fix .py files to use the correct equality test operator +fact.py Factorize numbers +find-uname.py Search for Unicode characters using regexps +from.py Summarize mailbox +lpwatch.py Watch BSD line printer queues +makedir.py Like mkdir -p +markov.py Markov chain simulation of words or characters +mboxconvert.py Convert MH or MMDF mailboxes to unix mailbox format +morse.py Produce morse code (as an AIFF file) +newslist.py List all newsgroups on a NNTP server as HTML pages +pi.py Print all digits of pi -- given enough time and memory +pp.py Emulate some Perl command line options +primes.py Print prime numbers +queens.py Dijkstra's solution to Wirth's "N Queens problem" +script.py Equivalent to BSD script(1) -- by Steen Lumholt +unbirthday.py Print unbirthday count +update.py Update a bunch of files according to a script Deleted: python/branches/py3k/Demo/scripts/toaiff.py ============================================================================== --- python/branches/py3k/Demo/scripts/toaiff.py Tue Aug 3 00:55:17 2010 +++ (empty file) @@ -1,107 +0,0 @@ -"""Convert "arbitrary" sound files to AIFF (Apple and SGI's audio format). - -Input may be compressed. -Uncompressed file type may be AIFF, WAV, VOC, 8SVX, NeXT/Sun, and others. -An exception is raised if the file is not of a recognized type. -Returned filename is either the input filename or a temporary filename; -in the latter case the caller must ensure that it is removed. -Other temporary files used are removed by the function. -""" - -import os -import tempfile -import pipes -import sndhdr - -__all__ = ["error", "toaiff"] - -table = {} - -t = pipes.Template() -t.append('sox -t au - -t aiff -r 8000 -', '--') -table['au'] = t - -# XXX The following is actually sub-optimal. -# XXX The HCOM sampling rate can be 22k, 22k/2, 22k/3 or 22k/4. -# XXX We must force the output sampling rate else the SGI won't play -# XXX files sampled at 5.5k or 7.333k; however this means that files -# XXX sampled at 11k are unnecessarily expanded. -# XXX Similar comments apply to some other file types. -t = pipes.Template() -t.append('sox -t hcom - -t aiff -r 22050 -', '--') -table['hcom'] = t - -t = pipes.Template() -t.append('sox -t voc - -t aiff -r 11025 -', '--') -table['voc'] = t - -t = pipes.Template() -t.append('sox -t wav - -t aiff -', '--') -table['wav'] = t - -t = pipes.Template() -t.append('sox -t 8svx - -t aiff -r 16000 -', '--') -table['8svx'] = t - -t = pipes.Template() -t.append('sox -t sndt - -t aiff -r 16000 -', '--') -table['sndt'] = t - -t = pipes.Template() -t.append('sox -t sndr - -t aiff -r 16000 -', '--') -table['sndr'] = t - -uncompress = pipes.Template() -uncompress.append('uncompress', '--') - - -class error(Exception): - pass - -def toaiff(filename): - temps = [] - ret = None - try: - ret = _toaiff(filename, temps) - finally: - for temp in temps[:]: - if temp != ret: - try: - os.unlink(temp) - except os.error: - pass - temps.remove(temp) - return ret - -def _toaiff(filename, temps): - if filename[-2:] == '.Z': - (fd, fname) = tempfile.mkstemp() - os.close(fd) - temps.append(fname) - sts = uncompress.copy(filename, fname) - if sts: - raise error(filename + ': uncompress failed') - else: - fname = filename - try: - ftype = sndhdr.whathdr(fname) - if ftype: - ftype = ftype[0] # All we're interested in - except IOError as msg: - if type(msg) == type(()) and len(msg) == 2 and \ - type(msg.args[0]) == type(0) and type(msg.args[1]) == type(''): - msg = msg.args[1] - if type(msg) != type(''): - msg = repr(msg) - raise error(filename + ': ' + msg) - if ftype == 'aiff': - return fname - if ftype is None or not ftype in table: - raise error('%s: unsupported audio file type %r' % (filename, ftype)) - (fd, temp) = tempfile.mkstemp() - os.close(fd) - temps.append(temp) - sts = table[ftype].copy(fname, temp) - if sts: - raise error(filename + ': conversion to aiff failed') - return temp From python-checkins at python.org Tue Aug 3 00:58:25 2010 From: python-checkins at python.org (georg.brandl) Date: Tue, 3 Aug 2010 00:58:25 +0200 (CEST) Subject: [Python-checkins] r83611 - in python/branches/py3k/Tools/framer/framer: bases.py template.py Message-ID: <20100802225825.BA4A0EEC77@mail.python.org> Author: georg.brandl Date: Tue Aug 3 00:58:25 2010 New Revision: 83611 Log: Make the framer run (still generates wrong code for module creation though.) Modified: python/branches/py3k/Tools/framer/framer/bases.py python/branches/py3k/Tools/framer/framer/template.py Modified: python/branches/py3k/Tools/framer/framer/bases.py ============================================================================== --- python/branches/py3k/Tools/framer/framer/bases.py (original) +++ python/branches/py3k/Tools/framer/framer/bases.py Tue Aug 3 00:58:25 2010 @@ -199,7 +199,7 @@ p(template.type_struct_start) for s in Slots[:-5]: # XXX val = self.__slots.get(s, s.default) - ntabs = 4 - (4 + len(val)) / 8 + ntabs = 4 - (4 + len(val)) // 8 line = " %s,%s/* %s */" % (val, "\t" * ntabs, s.name) print(line, file=f) p(template.type_struct_end) Modified: python/branches/py3k/Tools/framer/framer/template.py ============================================================================== --- python/branches/py3k/Tools/framer/framer/template.py (original) +++ python/branches/py3k/Tools/framer/framer/template.py Tue Aug 3 00:58:25 2010 @@ -76,7 +76,7 @@ module_init_start = """\ PyMODINIT_FUNC -init%(ModuleName)s(void) +PyInit_%(ModuleName)s(void) { PyObject *mod; From python-checkins at python.org Tue Aug 3 00:59:44 2010 From: python-checkins at python.org (georg.brandl) Date: Tue, 3 Aug 2010 00:59:44 +0200 (CEST) Subject: [Python-checkins] r83612 - python/branches/py3k/Tools/scripts/win_add2path.py Message-ID: <20100802225944.65A2FEEC8E@mail.python.org> Author: georg.brandl Date: Tue Aug 3 00:59:44 2010 New Revision: 83612 Log: Fix unicode literal. Modified: python/branches/py3k/Tools/scripts/win_add2path.py Modified: python/branches/py3k/Tools/scripts/win_add2path.py ============================================================================== --- python/branches/py3k/Tools/scripts/win_add2path.py (original) +++ python/branches/py3k/Tools/scripts/win_add2path.py Tue Aug 3 00:59:44 2010 @@ -15,7 +15,7 @@ HKCU = winreg.HKEY_CURRENT_USER ENV = "Environment" PATH = "PATH" -DEFAULT = u"%PATH%" +DEFAULT = "%PATH%" def modify(): pythonpath = os.path.dirname(os.path.normpath(sys.executable)) From python-checkins at python.org Tue Aug 3 01:13:12 2010 From: python-checkins at python.org (georg.brandl) Date: Tue, 3 Aug 2010 01:13:12 +0200 (CEST) Subject: [Python-checkins] r83613 - python/branches/py3k/Demo/README Message-ID: <20100802231312.A61D2EEC8D@mail.python.org> Author: georg.brandl Date: Tue Aug 3 01:13:12 2010 New Revision: 83613 Log: Update Demo README. Modified: python/branches/py3k/Demo/README Modified: python/branches/py3k/Demo/README ============================================================================== --- python/branches/py3k/Demo/README (original) +++ python/branches/py3k/Demo/README Tue Aug 3 01:13:12 2010 @@ -15,47 +15,51 @@ cgi CGI examples (see also ../Tools/faqwiz/.) -classes Some examples of how to use classes. +classes Some examples of how to use classes. -comparisons A set of responses to a really old language-comparison - challenge. +comparisons A set of responses to a really old language-comparison + challenge. -curses A set of curses demos. +curses A set of curses demos. -embed An example of embedding Python in another application - (see also pysvr). +distutils Test for using transparent 2to3 conversion in distutils. -imputil Demonstration subclasses of imputil.Importer. +embed An example of embedding Python in another application + (see also pysvr). -md5test Test program for the optional md5 module. +imputil Demonstration subclasses of imputil.Importer. -metaclasses The code from the 1.5 metaclasses paper on the web. +md5test Test program for the optional md5 module. -parser Example using the parser module. +newmetaclasses Demonstration of metaclasses. -pdist Old, unfinished code messing with CVS, RCS and remote - files. +parser Example using the parser module. -pysvr An example of embedding Python in a threaded - application. +pdist Old, unfinished code messing with CVS, RCS and remote + files. -rpc A set of classes for building clients and servers for - Sun RPC. +pysvr An example of embedding Python in a threaded + application. -scripts Some useful Python scripts that I put in my bin - directory. No optional built-in modules needed. +rpc A set of classes for building clients and servers for + Sun RPC. -sockets Examples for the new built-in module 'socket'. +scripts Some useful Python scripts that I put in my bin + directory. No optional built-in modules needed. -threads Demos that use the 'thread' module. (Currently these - only run on SGIs, but this may change in the future.) +sockets Examples for the new built-in module 'socket'. -tix Demos using the Tix widget set addition to Tkinter. +threads Demos that use the 'thread' module. (Currently these + only run on SGIs, but this may change in the future.) -tkinter Demos using the Tk interface (including Matt Conway's - excellent set of demos). +tix Demos using the Tix widget set addition to Tkinter. -xml Some XML demos. +tkinter Demos using the Tk interface (including Matt Conway's + excellent set of demos). -zlib Some demos for the zlib module (see also the standard - library module gzip.py). +turtle Demos for the "turtle" module. + +xml Some XML demos. + +zlib Some demos for the zlib module (see also the standard + library module gzip.py). From python-checkins at python.org Tue Aug 3 01:13:24 2010 From: python-checkins at python.org (georg.brandl) Date: Tue, 3 Aug 2010 01:13:24 +0200 (CEST) Subject: [Python-checkins] r83614 - python/branches/py3k/Demo/zlib/minigzip.py Message-ID: <20100802231324.9C7B3EEA5A@mail.python.org> Author: georg.brandl Date: Tue Aug 3 01:13:24 2010 New Revision: 83614 Log: Make minigzip work again. Modified: python/branches/py3k/Demo/zlib/minigzip.py Modified: python/branches/py3k/Demo/zlib/minigzip.py ============================================================================== --- python/branches/py3k/Demo/zlib/minigzip.py (original) +++ python/branches/py3k/Demo/zlib/minigzip.py Tue Aug 3 01:13:24 2010 @@ -10,10 +10,10 @@ FTEXT, FHCRC, FEXTRA, FNAME, FCOMMENT = 1, 2, 4, 8, 16 def write32(output, value): - output.write(chr(value & 255)) ; value=value // 256 - output.write(chr(value & 255)) ; value=value // 256 - output.write(chr(value & 255)) ; value=value // 256 - output.write(chr(value & 255)) + output.write(bytes([value & 255])) ; value=value // 256 + output.write(bytes([value & 255])) ; value=value // 256 + output.write(bytes([value & 255])) ; value=value // 256 + output.write(bytes([value & 255])) def read32(input): v = ord(input.read(1)) @@ -22,23 +22,24 @@ v += (ord(input.read(1)) << 24) return v -def compress (filename, input, output): - output.write('\037\213\010') # Write the header, ... - output.write(chr(FNAME)) # ... flag byte ... +def compress(filename, input, output): + output.write(b'\037\213\010') # Write the header, ... + output.write(bytes([FNAME])) # ... flag byte ... - statval = os.stat(filename) # ... modification time ... + statval = os.stat(filename) # ... modification time ... mtime = statval[8] write32(output, mtime) - output.write('\002') # ... slowest compression alg. ... - output.write('\377') # ... OS (=unknown) ... - output.write(filename+'\000') # ... original filename ... + output.write(b'\002') # ... slowest compression alg. ... + output.write(b'\377') # ... OS (=unknown) ... + bfilename = filename.encode(sys.getfilesystemencoding()) + output.write(bfilename + b'\000') # ... original filename ... - crcval = zlib.crc32("") + crcval = zlib.crc32(b'') compobj = zlib.compressobj(9, zlib.DEFLATED, -zlib.MAX_WBITS, zlib.DEF_MEM_LEVEL, 0) while True: data = input.read(1024) - if data == "": + if data == b'': break crcval = zlib.crc32(data, crcval) output.write(compobj.compress(data)) @@ -46,9 +47,9 @@ write32(output, crcval) # ... the CRC ... write32(output, statval[6]) # and the file size. -def decompress (input, output): +def decompress(input, output): magic = input.read(2) - if magic != '\037\213': + if magic != b'\037\213': print('Not a gzipped file') sys.exit(0) if ord(input.read(1)) != 8: @@ -66,21 +67,21 @@ # Read and discard a null-terminated string containing the filename while True: s = input.read(1) - if s == '\0': break + if s == b'\0': break if flag & FCOMMENT: # Read and discard a null-terminated string containing a comment while True: - s=input.read(1) - if s=='\0': break + s = input.read(1) + if s == b'\0': break if flag & FHCRC: input.read(2) # Read & discard the 16-bit header CRC decompobj = zlib.decompressobj(-zlib.MAX_WBITS) - crcval = zlib.crc32("") + crcval = zlib.crc32(b'') length = 0 while True: - data=input.read(1024) - if data == "": + data = input.read(1024) + if data == b"": break decompdata = decompobj.decompress(data) output.write(decompdata) From python-checkins at python.org Tue Aug 3 01:15:58 2010 From: python-checkins at python.org (georg.brandl) Date: Tue, 3 Aug 2010 01:15:58 +0200 (CEST) Subject: [Python-checkins] r83615 - python/branches/py3k/Demo/zlib/zlibdemo.py Message-ID: <20100802231558.F4195EEC8B@mail.python.org> Author: georg.brandl Date: Tue Aug 3 01:15:58 2010 New Revision: 83615 Log: Another demo that at least runs again. Modified: python/branches/py3k/Demo/zlib/zlibdemo.py Modified: python/branches/py3k/Demo/zlib/zlibdemo.py ============================================================================== --- python/branches/py3k/Demo/zlib/zlibdemo.py (original) +++ python/branches/py3k/Demo/zlib/zlibdemo.py Tue Aug 3 01:15:58 2010 @@ -13,9 +13,8 @@ filename = sys.argv[0] print('Reading', filename) - f = open(filename, 'rb') # Get the data to compress - s = f.read() - f.close() + with open(filename, 'rb') as f: # Get the data to compress + s = f.read() # First, we'll compress the string in one step comptext = zlib.compress(s, 1) @@ -30,15 +29,15 @@ chunk = 256 compressor = zlib.compressobj(9) decompressor = zlib.decompressobj() - comptext = decomp = '' + comptext = decomp = b'' for i in range(0, len(s), chunk): - comptext = comptext+compressor.compress(s[i:i+chunk]) + comptext += compressor.compress(s[i:i+chunk]) # Don't forget to call flush()!! - comptext = comptext + compressor.flush() + comptext += compressor.flush() for i in range(0, len(comptext), chunk): - decomp = decomp + decompressor.decompress(comptext[i:i+chunk]) - decomp=decomp+decompressor.flush() + decomp += decompressor.decompress(comptext[i:i+chunk]) + decomp += decompressor.flush() print('Progressive compression (level 9):') print(' Original:', len(s), 'Compressed:', len(comptext), end=' ') From python-checkins at python.org Tue Aug 3 01:17:21 2010 From: python-checkins at python.org (georg.brandl) Date: Tue, 3 Aug 2010 01:17:21 +0200 (CEST) Subject: [Python-checkins] r83616 - in python/branches/py3k/Demo: README pdist Message-ID: <20100802231721.C7AE7EECA8@mail.python.org> Author: georg.brandl Date: Tue Aug 3 01:17:21 2010 New Revision: 83616 Log: Remove obsolete pdist demo. Removed: python/branches/py3k/Demo/pdist/ Modified: python/branches/py3k/Demo/README Modified: python/branches/py3k/Demo/README ============================================================================== --- python/branches/py3k/Demo/README (original) +++ python/branches/py3k/Demo/README Tue Aug 3 01:17:21 2010 @@ -35,9 +35,6 @@ parser Example using the parser module. -pdist Old, unfinished code messing with CVS, RCS and remote - files. - pysvr An example of embedding Python in a threaded application. From python-checkins at python.org Tue Aug 3 01:18:35 2010 From: python-checkins at python.org (georg.brandl) Date: Tue, 3 Aug 2010 01:18:35 +0200 (CEST) Subject: [Python-checkins] r83617 - python/branches/py3k/Demo/README Message-ID: <20100802231835.4FDE1EEC99@mail.python.org> Author: georg.brandl Date: Tue Aug 3 01:18:35 2010 New Revision: 83617 Log: Remove reference to removed faqwiz tool. Modified: python/branches/py3k/Demo/README Modified: python/branches/py3k/Demo/README ============================================================================== --- python/branches/py3k/Demo/README (original) +++ python/branches/py3k/Demo/README Tue Aug 3 01:18:35 2010 @@ -13,7 +13,7 @@ in case I change my mind about them. -cgi CGI examples (see also ../Tools/faqwiz/.) +cgi CGI examples. classes Some examples of how to use classes. From python-checkins at python.org Tue Aug 3 01:30:09 2010 From: python-checkins at python.org (georg.brandl) Date: Tue, 3 Aug 2010 01:30:09 +0200 (CEST) Subject: [Python-checkins] r83618 - in python/branches/py3k/Demo/tkinter/guido: AttrDialog.py ManPage.py brownian2.py kill.py Message-ID: <20100802233009.52142EECA9@mail.python.org> Author: georg.brandl Date: Tue Aug 3 01:30:09 2010 New Revision: 83618 Log: Fix-up some tkinter demos. Modified: python/branches/py3k/Demo/tkinter/guido/AttrDialog.py python/branches/py3k/Demo/tkinter/guido/ManPage.py python/branches/py3k/Demo/tkinter/guido/brownian2.py python/branches/py3k/Demo/tkinter/guido/kill.py Modified: python/branches/py3k/Demo/tkinter/guido/AttrDialog.py ============================================================================== --- python/branches/py3k/Demo/tkinter/guido/AttrDialog.py (original) +++ python/branches/py3k/Demo/tkinter/guido/AttrDialog.py Tue Aug 3 01:30:09 2010 @@ -120,7 +120,7 @@ cl = self.classes[c] except KeyError: cl = 'unknown' - if type(cl) == TupleType: + if type(cl) == tuple: cl = self.enumoption elif cl == 'boolean': cl = self.booleanoption Modified: python/branches/py3k/Demo/tkinter/guido/ManPage.py ============================================================================== --- python/branches/py3k/Demo/tkinter/guido/ManPage.py (original) +++ python/branches/py3k/Demo/tkinter/guido/ManPage.py Tue Aug 3 01:30:09 2010 @@ -107,13 +107,13 @@ # Save this line -- we need one line read-ahead self.buffer = nextline return - if emptyprog.match(self.buffer) >= 0: + if emptyprog.match(self.buffer): # Buffered line was empty -- set a flag self.empty = 1 self.buffer = nextline return textline = self.buffer - if ulprog.match(nextline) >= 0: + if ulprog.match(nextline): # Next line is properties for buffered line propline = nextline self.buffer = None @@ -127,7 +127,7 @@ self.ok = 1 self.empty = 0 return - if footerprog.match(textline) >= 0: + if footerprog.match(textline): # Footer -- start skipping until next non-blank line self.ok = 0 self.empty = 0 @@ -190,7 +190,7 @@ import os import sys # XXX This directory may be different on your system - MANDIR = '/usr/local/man/mann' + MANDIR = '' DEFAULTPAGE = 'Tcl' formatted = 0 if sys.argv[1:] and sys.argv[1] == '-f': Modified: python/branches/py3k/Demo/tkinter/guido/brownian2.py ============================================================================== --- python/branches/py3k/Demo/tkinter/guido/brownian2.py (original) +++ python/branches/py3k/Demo/tkinter/guido/brownian2.py Tue Aug 3 01:30:09 2010 @@ -32,7 +32,7 @@ yield None def move(particle): # move the particle at random time - particle.next() + next(particle) dt = random.expovariate(LAMBDA) root.after(int(dt*1000), move, particle) Modified: python/branches/py3k/Demo/tkinter/guido/kill.py ============================================================================== --- python/branches/py3k/Demo/tkinter/guido/kill.py (original) +++ python/branches/py3k/Demo/tkinter/guido/kill.py Tue Aug 3 01:30:09 2010 @@ -2,8 +2,6 @@ # Tkinter interface to Linux `kill' command. from tkinter import * -from string import splitfields -from string import split import subprocess import os @@ -26,13 +24,13 @@ ('Hex', '-X', 0)] def kill(self, selected): c = self.format_list[self.format.get()][2] - pid = split(selected)[c] + pid = selected.split()[c] os.system('kill -9 ' + pid) self.do_update() def do_update(self): name, option, column = self.format_list[self.format.get()] s = subprocess.getoutput('ps -w ' + option) - list = splitfields(s, '\n') + list = s.split('\n') self.header.set(list[0]) del list[0] y = self.frame.vscroll.get()[0] From python-checkins at python.org Tue Aug 3 01:34:50 2010 From: python-checkins at python.org (ezio.melotti) Date: Tue, 3 Aug 2010 01:34:50 +0200 (CEST) Subject: [Python-checkins] r83619 - in python/branches/release26-maint: Lib/test/test_coercion.py Lib/test/test_commands.py Lib/test/test_complex.py Lib/test/test_contextlib.py Lib/test/test_cookie.py Lib/test/test_ctypes.py Lib/test/test_descr.py Lib/test/test_doctest.py Lib/test/test_exceptions.py Lib/test/test_global.py Lib/test/test_int_literal.py Lib/test/test_io.py Lib/test/test_macostools.py Lib/test/test_pep352.py Lib/test/test_re.py Lib/test/test_sundry.py Lib/test/test_symtable.py Lib/test/test_urllibnet.py Lib/test/test_warnings.py Lib/test/test_zipimport_support.py Message-ID: <20100802233450.4A48EEECA3@mail.python.org> Author: ezio.melotti Date: Tue Aug 3 01:34:49 2010 New Revision: 83619 Log: Merged revisions 79539 via svnmerge from svn+ssh://pythondev at svn.python.org/python/trunk ........ r79539 | florent.xicluna | 2010-04-01 01:01:03 +0300 (Thu, 01 Apr 2010) | 2 lines Replace catch_warnings with check_warnings when it makes sense. Use assertRaises context manager to simplify some tests. ........ Modified: python/branches/release26-maint/ (props changed) python/branches/release26-maint/Lib/test/test_coercion.py python/branches/release26-maint/Lib/test/test_commands.py python/branches/release26-maint/Lib/test/test_complex.py python/branches/release26-maint/Lib/test/test_contextlib.py python/branches/release26-maint/Lib/test/test_cookie.py python/branches/release26-maint/Lib/test/test_ctypes.py python/branches/release26-maint/Lib/test/test_descr.py python/branches/release26-maint/Lib/test/test_doctest.py python/branches/release26-maint/Lib/test/test_exceptions.py python/branches/release26-maint/Lib/test/test_global.py python/branches/release26-maint/Lib/test/test_int_literal.py python/branches/release26-maint/Lib/test/test_io.py python/branches/release26-maint/Lib/test/test_macostools.py python/branches/release26-maint/Lib/test/test_pep352.py python/branches/release26-maint/Lib/test/test_re.py python/branches/release26-maint/Lib/test/test_sundry.py python/branches/release26-maint/Lib/test/test_symtable.py python/branches/release26-maint/Lib/test/test_urllibnet.py python/branches/release26-maint/Lib/test/test_warnings.py python/branches/release26-maint/Lib/test/test_zipimport_support.py Modified: python/branches/release26-maint/Lib/test/test_coercion.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_coercion.py (original) +++ python/branches/release26-maint/Lib/test/test_coercion.py Tue Aug 3 01:34:49 2010 @@ -1,7 +1,6 @@ import copy -import warnings import unittest -from test.test_support import run_unittest, TestFailed +from test.test_support import run_unittest, TestFailed, check_warnings # Fake a number that implements numeric methods through __coerce__ class CoerceNumber: @@ -223,12 +222,6 @@ infix_results[key] = res -with warnings.catch_warnings(): - warnings.filterwarnings("ignore", "classic int division", - DeprecationWarning) - process_infix_results() -# now infix_results has two lists of results for every pairing. - prefix_binops = [ 'divmod' ] prefix_results = [ [(1,0), (1L,0L), (0.0,2.0), ((1+0j),0j), TE, TE, TE, TE, (1,0)], @@ -339,11 +332,13 @@ raise exc def test_main(): - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", "complex divmod.., // and % " - "are deprecated", DeprecationWarning) - warnings.filterwarnings("ignore", "classic (int|long) division", - DeprecationWarning) + with check_warnings(("complex divmod.., // and % are deprecated", + DeprecationWarning), + ("classic (int|long) division", DeprecationWarning), + quiet=True): + process_infix_results() + # now infix_results has two lists of results for every pairing. + run_unittest(CoercionTest) if __name__ == "__main__": Modified: python/branches/release26-maint/Lib/test/test_commands.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_commands.py (original) +++ python/branches/release26-maint/Lib/test/test_commands.py Tue Aug 3 01:34:49 2010 @@ -4,13 +4,11 @@ ''' import unittest import os, tempfile, re -import warnings -warnings.filterwarnings('ignore', r".*commands.getstatus.. is deprecated", - DeprecationWarning) +from test.test_support import run_unittest, reap_children, import_module, \ + check_warnings from test.test_support import TestSkipped, run_unittest, reap_children, import_module - # Silence Py3k warning import_module('commands', deprecated=True) from commands import * @@ -60,7 +58,11 @@ /\. # and end with the name of the file. ''' - self.assert_(re.match(pat, getstatus("/."), re.VERBOSE)) + with check_warnings((".*commands.getstatus.. is deprecated", + DeprecationWarning), + ("in 3.x, mkarg has been removed", + DeprecationWarning),): + self.assertTrue(re.match(pat, getstatus("/."), re.VERBOSE)) def test_main(): Modified: python/branches/release26-maint/Lib/test/test_complex.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_complex.py (original) +++ python/branches/release26-maint/Lib/test/test_complex.py Tue Aug 3 01:34:49 2010 @@ -1,13 +1,6 @@ -import unittest, os +import unittest from test import test_support -import warnings -warnings.filterwarnings( - "ignore", - category=DeprecationWarning, - message=".*complex divmod.*are deprecated" -) - from random import random from math import atan2 @@ -371,10 +364,7 @@ finally: if (fo is not None) and (not fo.closed): fo.close() - try: - os.remove(test_support.TESTFN) - except (OSError, IOError): - pass + test_support.unlink(test_support.TESTFN) def test_getnewargs(self): self.assertEqual((1+2j).__getnewargs__(), (1.0, 2.0)) @@ -392,7 +382,9 @@ self.assertEquals(atan2(z2.imag, -1.), atan2(-0., -1.)) def test_main(): - test_support.run_unittest(ComplexTest) + with test_support.check_warnings(("complex divmod.., // and % are " + "deprecated", DeprecationWarning)): + test_support.run_unittest(ComplexTest) if __name__ == "__main__": test_main() Modified: python/branches/release26-maint/Lib/test/test_contextlib.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_contextlib.py (original) +++ python/branches/release26-maint/Lib/test/test_contextlib.py Tue Aug 3 01:34:49 2010 @@ -1,6 +1,5 @@ """Unit tests for contextlib.py, and other context managers.""" - import sys import os import decimal @@ -139,7 +138,7 @@ with nested(a(), b()) as (x, y): state.append(x) state.append(y) - 1/0 + 1 // 0 except ZeroDivisionError: self.assertEqual(state, [1, 4, 2, 5, 6, 3]) else: @@ -160,7 +159,7 @@ pass try: with nested(a(), b()) as (x, y): - 1/0 + 1 // 0 except ZeroDivisionError: self.assertEqual((x, y), (1, 2)) except Exception: @@ -181,7 +180,7 @@ pass try: with nested(a(), b()): - 1/0 + 1 // 0 except ZeroDivisionError: self.fail("Didn't swallow ZeroDivisionError") @@ -247,7 +246,7 @@ try: with closing(x) as y: self.assertEqual(x, y) - 1/0 + 1 // 0 except ZeroDivisionError: self.assertEqual(state, [1]) else: @@ -268,7 +267,7 @@ with open(tfn, "r") as f: self.failIf(f.closed) self.assertEqual(f.read(), "Booh\n") - 1/0 + 1 // 0 except ZeroDivisionError: self.failUnless(f.closed) else: @@ -289,7 +288,7 @@ try: with lock: self.failUnless(locked()) - 1/0 + 1 // 0 except ZeroDivisionError: self.failIf(locked()) else: @@ -333,5 +332,6 @@ def test_main(): test_support.run_unittest(__name__) + if __name__ == "__main__": test_main() Modified: python/branches/release26-maint/Lib/test/test_cookie.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_cookie.py (original) +++ python/branches/release26-maint/Lib/test/test_cookie.py Tue Aug 3 01:34:49 2010 @@ -1,13 +1,9 @@ # Simple test suite for Cookie.py -from test.test_support import run_unittest, run_doctest +from test.test_support import run_unittest, run_doctest, check_warnings import unittest import Cookie -import warnings -warnings.filterwarnings("ignore", - ".* class is insecure.*", - DeprecationWarning) class CookieTests(unittest.TestCase): # Currently this only tests SimpleCookie @@ -86,7 +82,9 @@ def test_main(): run_unittest(CookieTests) - run_doctest(Cookie) + with check_warnings(('.+Cookie class is insecure; do not use it', + DeprecationWarning)): + run_doctest(Cookie) if __name__ == '__main__': test_main() Modified: python/branches/release26-maint/Lib/test/test_ctypes.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_ctypes.py (original) +++ python/branches/release26-maint/Lib/test/test_ctypes.py Tue Aug 3 01:34:49 2010 @@ -7,7 +7,7 @@ def test_main(): with _check_py3k_warnings(("buffer.. not supported", DeprecationWarning), - ("classic (int|long) division", DeprecationWarning)): + ("classic (int|long) division", DeprecationWarning),): import ctypes.test skipped, testcases = ctypes.test.get_tests(ctypes.test, "test_*.py", verbosity=0) suites = [unittest.makeSuite(t) for t in testcases] Modified: python/branches/release26-maint/Lib/test/test_descr.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_descr.py (original) +++ python/branches/release26-maint/Lib/test/test_descr.py Tue Aug 3 01:34:49 2010 @@ -1,7 +1,7 @@ import __builtin__ +import sys import types import unittest -import warnings from copy import deepcopy from test import test_support @@ -58,15 +58,6 @@ expr = '%s a' % expr self.unops[name] = expr - def setUp(self): - self.original_filters = warnings.filters[:] - warnings.filterwarnings("ignore", - r'complex divmod\(\), // and % are deprecated$', - DeprecationWarning, r'(|%s)$' % __name__) - - def tearDown(self): - warnings.filters = self.original_filters - def unop_test(self, a, res, expr="len(a)", meth="__len__"): d = {'a': a} self.assertEqual(eval(expr, d), res) @@ -4433,10 +4424,14 @@ def test_main(): - with test_support._check_py3k_warnings( + deprecations = [(r'complex divmod\(\), // and % are deprecated$', + DeprecationWarning)] + if sys.py3kwarning: + deprecations += [ ("classic (int|long) division", DeprecationWarning), ("coerce.. not supported", DeprecationWarning), - (".+__(get|set|del)slice__ has been removed", DeprecationWarning)): + (".+__(get|set|del)slice__ has been removed", DeprecationWarning)] + with test_support.check_warnings(*deprecations): # Run all local test cases, with PTypesLongInitTest first. test_support.run_unittest(PTypesLongInitTest, OperatorsTest, ClassPropertiesAndMethods, DictProxyTests) Modified: python/branches/release26-maint/Lib/test/test_doctest.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_doctest.py (original) +++ python/branches/release26-maint/Lib/test/test_doctest.py Tue Aug 3 01:34:49 2010 @@ -3,9 +3,9 @@ Test script for doctest. """ +import sys from test import test_support import doctest -import warnings # NOTE: There are some additional tests relating to interaction with # zipimport in the test_zipimport_support test module. @@ -2511,12 +2511,12 @@ test_support.run_doctest(doctest, verbosity=True) from test import test_doctest - with test_support._check_py3k_warnings( - ("backquote not supported", SyntaxWarning), - ("execfile.. not supported", DeprecationWarning)): - # Ignore all warnings about the use of class Tester in this module. - warnings.filterwarnings("ignore", "class Tester is deprecated", - DeprecationWarning) + # Ignore all warnings about the use of class Tester in this module. + deprecations = [("class Tester is deprecated", DeprecationWarning)] + if sys.py3kwarning: + deprecations += [("backquote not supported", SyntaxWarning), + ("execfile.. not supported", DeprecationWarning)] + with test_support.check_warnings(*deprecations): # Check the doctest cases defined here: test_support.run_doctest(test_doctest, verbosity=True) Modified: python/branches/release26-maint/Lib/test/test_exceptions.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_exceptions.py (original) +++ python/branches/release26-maint/Lib/test/test_exceptions.py Tue Aug 3 01:34:49 2010 @@ -4,9 +4,9 @@ import sys import unittest import pickle, cPickle -import warnings -from test.test_support import TESTFN, unlink, run_unittest, captured_output +from test.test_support import (TESTFN, unlink, run_unittest, captured_output, + check_warnings) from test.test_pep352 import ignore_deprecation_warnings # XXX This is not really enough, each *operation* should be tested! @@ -308,23 +308,19 @@ # Accessing BaseException.message and relying on its value set by # BaseException.__init__ triggers a deprecation warning. exc = BaseException("foo") - with warnings.catch_warnings(record=True) as w: - self.assertEquals(exc.message, "foo") - self.assertEquals(len(w), 1) - self.assertEquals(w[0].category, DeprecationWarning) - self.assertEquals( - str(w[0].message), - "BaseException.message has been deprecated as of Python 2.6") - + with check_warnings(("BaseException.message has been deprecated " + "as of Python 2.6", DeprecationWarning)) as w: + self.assertEqual(exc.message, "foo") + self.assertEqual(len(w.warnings), 1) def testRegularMessageAttribute(self): # Accessing BaseException.message after explicitly setting a value # for it does not trigger a deprecation warning. exc = BaseException("foo") exc.message = "bar" - with warnings.catch_warnings(record=True) as w: - self.assertEquals(exc.message, "bar") - self.assertEquals(len(w), 0) + with check_warnings(quiet=True) as w: + self.assertEqual(exc.message, "bar") + self.assertEqual(len(w.warnings), 0) # Deleting the message is supported, too. del exc.message self.assertRaises(AttributeError, getattr, exc, "message") Modified: python/branches/release26-maint/Lib/test/test_global.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_global.py (original) +++ python/branches/release26-maint/Lib/test/test_global.py Tue Aug 3 01:34:49 2010 @@ -2,9 +2,8 @@ from test.test_support import run_unittest, check_syntax_error import unittest - import warnings -warnings.filterwarnings("error", module="") + class GlobalTests(unittest.TestCase): @@ -45,7 +44,9 @@ def test_main(): - run_unittest(GlobalTests) + with warnings.catch_warnings(): + warnings.filterwarnings("error", module="") + run_unittest(GlobalTests) if __name__ == "__main__": test_main() Modified: python/branches/release26-maint/Lib/test/test_int_literal.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_int_literal.py (original) +++ python/branches/release26-maint/Lib/test/test_int_literal.py Tue Aug 3 01:34:49 2010 @@ -6,9 +6,6 @@ import unittest from test import test_support -import warnings -warnings.filterwarnings("ignore", "hex/oct constants", FutureWarning, - "") class TestHexOctBin(unittest.TestCase): Modified: python/branches/release26-maint/Lib/test/test_io.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_io.py (original) +++ python/branches/release26-maint/Lib/test/test_io.py Tue Aug 3 01:34:49 2010 @@ -9,7 +9,7 @@ import threading import random import unittest -from itertools import chain, cycle +from itertools import cycle, count from test import test_support import codecs Modified: python/branches/release26-maint/Lib/test/test_macostools.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_macostools.py (original) +++ python/branches/release26-maint/Lib/test/test_macostools.py Tue Aug 3 01:34:49 2010 @@ -21,14 +21,8 @@ rfp.close() def tearDown(self): - try: - os.unlink(test_support.TESTFN) - except: - pass - try: - os.unlink(TESTFN2) - except: - pass + test_support.unlink(test_support.TESTFN) + test_support.unlink(TESTFN2) def compareData(self): fp = open(test_support.TESTFN, 'r') @@ -51,36 +45,25 @@ def test_touched(self): # This really only tests that nothing unforeseen happens. - import warnings - with warnings.catch_warnings(): - warnings.filterwarnings('ignore', 'macostools.touched*', - DeprecationWarning) + with test_support.check_warnings(('macostools.touched*', + DeprecationWarning), quiet=True): macostools.touched(test_support.TESTFN) if sys.maxint < 2**32: def test_copy(self): - try: - os.unlink(TESTFN2) - except: - pass + test_support.unlink(TESTFN2) macostools.copy(test_support.TESTFN, TESTFN2) self.assertEqual(self.compareData(), '') if sys.maxint < 2**32: def test_mkalias(self): - try: - os.unlink(TESTFN2) - except: - pass + test_support.unlink(TESTFN2) macostools.mkalias(test_support.TESTFN, TESTFN2) fss, _, _ = Carbon.File.ResolveAliasFile(TESTFN2, 0) self.assertEqual(fss.as_pathname(), os.path.realpath(test_support.TESTFN)) def test_mkalias_relative(self): - try: - os.unlink(TESTFN2) - except: - pass + test_support.unlink(TESTFN2) # If the directory doesn't exist, then chances are this is a new # install of Python so don't create it since the user might end up # running ``sudo make install`` and creating the directory here won't Modified: python/branches/release26-maint/Lib/test/test_pep352.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_pep352.py (original) +++ python/branches/release26-maint/Lib/test/test_pep352.py Tue Aug 3 01:34:49 2010 @@ -2,7 +2,7 @@ import __builtin__ import exceptions import warnings -from test.test_support import run_unittest +from test.test_support import run_unittest, check_warnings import os import sys from platform import system as platform_system @@ -15,14 +15,13 @@ "catching classes that don't inherit from BaseException is not allowed", "__get(item|slice)__ not supported for exception classes"]) +_deprecations = [(msg, DeprecationWarning) for msg in DEPRECATION_WARNINGS] + # Silence Py3k and other deprecation warnings def ignore_deprecation_warnings(func): """Ignore the known DeprecationWarnings.""" def wrapper(*args, **kw): - with warnings.catch_warnings(): - warnings.resetwarnings() - for text in DEPRECATION_WARNINGS: - warnings.filterwarnings("ignore", text, DeprecationWarning) + with check_warnings(*_deprecations, quiet=True): return func(*args, **kw) return wrapper @@ -139,16 +138,8 @@ def test_message_deprecation(self): # As of Python 2.6, BaseException.message is deprecated. - with warnings.catch_warnings(): - warnings.resetwarnings() - warnings.filterwarnings('error') - - try: - BaseException().message - except DeprecationWarning: - pass - else: - self.fail("BaseException.message not deprecated") + with check_warnings(("", DeprecationWarning)): + BaseException().message class UsageTests(unittest.TestCase): Modified: python/branches/release26-maint/Lib/test/test_re.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_re.py (original) +++ python/branches/release26-maint/Lib/test/test_re.py Tue Aug 3 01:34:49 2010 @@ -1,7 +1,4 @@ -import sys -sys.path = ['.'] + sys.path - -from test.test_support import verbose, run_unittest +from test.test_support import verbose, run_unittest, import_module import re from re import Scanner import sys, os, traceback @@ -450,11 +447,8 @@ import cPickle self.pickle_test(cPickle) # old pickles expect the _compile() reconstructor in sre module - import warnings - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", "The sre module is deprecated", - DeprecationWarning) - from sre import _compile + import_module("sre", deprecated=True) + from sre import _compile def pickle_test(self, pickle): oldpat = re.compile('a(?:b|(c|e){1,2}?|d)+?(.)') Modified: python/branches/release26-maint/Lib/test/test_sundry.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_sundry.py (original) +++ python/branches/release26-maint/Lib/test/test_sundry.py Tue Aug 3 01:34:49 2010 @@ -3,12 +3,11 @@ from test import test_support import sys import unittest -import warnings class TestUntestedModules(unittest.TestCase): def test_at_least_import_untested_modules(self): - with warnings.catch_warnings(): + with test_support.check_warnings(quiet=True): import CGIHTTPServer import audiodev import bdb Modified: python/branches/release26-maint/Lib/test/test_symtable.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_symtable.py (original) +++ python/branches/release26-maint/Lib/test/test_symtable.py Tue Aug 3 01:34:49 2010 @@ -44,9 +44,8 @@ class SymtableTest(unittest.TestCase): - with warnings.catch_warnings(): - # Ignore warnings about "from blank import *" - warnings.simplefilter("ignore", SyntaxWarning) + with test_support.check_warnings( + ("import \* only allowed at module level", SyntaxWarning)): top = symtable.symtable(TEST_CODE, "?", "exec") # These correspond to scopes in TEST_CODE Mine = find_block(top, "Mine") Modified: python/branches/release26-maint/Lib/test/test_urllibnet.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_urllibnet.py (original) +++ python/branches/release26-maint/Lib/test/test_urllibnet.py Tue Aug 3 01:34:49 2010 @@ -193,10 +193,8 @@ def test_main(): test_support.requires('network') - from warnings import filterwarnings, catch_warnings - with catch_warnings(): - filterwarnings('ignore', '.*urllib\.urlopen.*Python 3.0', - DeprecationWarning) + with test_support.check_py3k_warnings( + ("urllib.urlopen.. has been removed", DeprecationWarning)): test_support.run_unittest(URLTimeoutTest, urlopenNetworkTests, urlretrieveNetworkTests) Modified: python/branches/release26-maint/Lib/test/test_warnings.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_warnings.py (original) +++ python/branches/release26-maint/Lib/test/test_warnings.py Tue Aug 3 01:34:49 2010 @@ -620,14 +620,21 @@ with test_support.check_warnings(('foo', UserWarning)): wmod.warn("foo") - with self.assertRaises(AssertionError): + try: with test_support.check_warnings(('', RuntimeWarning)): # defaults to quiet=False with argument pass - with self.assertRaises(AssertionError): + except AssertionError: + pass + else: + self.fail("Dind't raise AssertionError") + try: with test_support.check_warnings(('foo', RuntimeWarning)): wmod.warn("foo") - + except AssertionError: + pass + else: + self.fail("Dind't raise AssertionError") class CCatchWarningTests(CatchWarningTests): module = c_warnings Modified: python/branches/release26-maint/Lib/test/test_zipimport_support.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_zipimport_support.py (original) +++ python/branches/release26-maint/Lib/test/test_zipimport_support.py Tue Aug 3 01:34:49 2010 @@ -172,12 +172,14 @@ test_zipped_doctest.test_unittest_reportflags, ] # Needed for test_DocTestParser and test_debug - with test.test_support._check_py3k_warnings( - ("backquote not supported", SyntaxWarning), - ("execfile.. not supported", DeprecationWarning)): + deprecations = [ # Ignore all warnings about the use of class Tester in this module. - warnings.filterwarnings("ignore", "class Tester is deprecated", - DeprecationWarning) + ("class Tester is deprecated", DeprecationWarning)] + if sys.py3kwarning: + deprecations += [ + ("backquote not supported", SyntaxWarning), + ("execfile.. not supported", DeprecationWarning)] + with test.test_support.check_warnings(*deprecations): for obj in known_good_tests: _run_object_doctest(obj, test_zipped_doctest) From python-checkins at python.org Tue Aug 3 01:39:23 2010 From: python-checkins at python.org (ezio.melotti) Date: Tue, 3 Aug 2010 01:39:23 +0200 (CEST) Subject: [Python-checkins] r83620 - python/branches/release26-maint/Lib/bsddb/test/test_recno.py Message-ID: <20100802233923.B6A11EECA9@mail.python.org> Author: ezio.melotti Date: Tue Aug 3 01:39:23 2010 New Revision: 83620 Log: Fix test_recno. Modified: python/branches/release26-maint/Lib/bsddb/test/test_recno.py Modified: python/branches/release26-maint/Lib/bsddb/test/test_recno.py ============================================================================== --- python/branches/release26-maint/Lib/bsddb/test/test_recno.py (original) +++ python/branches/release26-maint/Lib/bsddb/test/test_recno.py Tue Aug 3 01:39:23 2010 @@ -38,7 +38,7 @@ for x in letters: recno = d.append(x * 60) - self.assertIsInstance(recno, int) + self.assertTrue(isinstance(recno, int)) self.assertGreaterEqual(recno, 1) if verbose: print recno, @@ -54,7 +54,7 @@ if verbose: print data - self.assertIsInstance(data, str) + self.assertTrue(isinstance(data, str)) self.assertEqual(data, d.get(recno)) try: @@ -91,18 +91,18 @@ keys = d.keys() if verbose: print keys - self.assertIsInstance(keys, list) - self.assertIsInstance(keys[0], int) + self.assertTrue(isinstance(keys, list)) + self.assertTrue(isinstance(keys[0], int)) self.assertEqual(len(keys), len(d)) items = d.items() if verbose: pprint(items) - self.assertIsInstance(items, list) - self.assertIsInstance(items[0], tuple) + self.assertTrue(isinstance(items, list)) + self.assertTrue(isinstance(items[0], tuple)) self.assertEqual(len(items[0]), 2) - self.assertIsInstance(items[0][0], int) - self.assertIsInstance(items[0][1], str) + self.assertTrue(isinstance(items[0][0], int)) + self.assertTrue(isinstance(items[0][1], str)) self.assertEqual(len(items), len(d)) self.assertTrue(d.has_key(25)) From python-checkins at python.org Tue Aug 3 02:18:09 2010 From: python-checkins at python.org (ezio.melotti) Date: Tue, 3 Aug 2010 02:18:09 +0200 (CEST) Subject: [Python-checkins] r83621 - in python/branches/release26-maint/Lib: bsddb/test/test_recno.py test/test_urllibnet.py Message-ID: <20100803001809.329BAEECBB@mail.python.org> Author: ezio.melotti Date: Tue Aug 3 02:18:09 2010 New Revision: 83621 Log: Fix a couple more tests. Modified: python/branches/release26-maint/Lib/bsddb/test/test_recno.py python/branches/release26-maint/Lib/test/test_urllibnet.py Modified: python/branches/release26-maint/Lib/bsddb/test/test_recno.py ============================================================================== --- python/branches/release26-maint/Lib/bsddb/test/test_recno.py (original) +++ python/branches/release26-maint/Lib/bsddb/test/test_recno.py Tue Aug 3 02:18:09 2010 @@ -39,7 +39,7 @@ for x in letters: recno = d.append(x * 60) self.assertTrue(isinstance(recno, int)) - self.assertGreaterEqual(recno, 1) + self.assertTrue(recno >= 1) if verbose: print recno, Modified: python/branches/release26-maint/Lib/test/test_urllibnet.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_urllibnet.py (original) +++ python/branches/release26-maint/Lib/test/test_urllibnet.py Tue Aug 3 02:18:09 2010 @@ -193,7 +193,7 @@ def test_main(): test_support.requires('network') - with test_support.check_py3k_warnings( + with test_support._check_py3k_warnings( ("urllib.urlopen.. has been removed", DeprecationWarning)): test_support.run_unittest(URLTimeoutTest, urlopenNetworkTests, From python-checkins at python.org Tue Aug 3 02:52:46 2010 From: python-checkins at python.org (ezio.melotti) Date: Tue, 3 Aug 2010 02:52:46 +0200 (CEST) Subject: [Python-checkins] r83622 - python/branches/release26-maint/Lib/test/test_commands.py Message-ID: <20100803005246.E89B3EEC70@mail.python.org> Author: ezio.melotti Date: Tue Aug 3 02:52:46 2010 New Revision: 83622 Log: Remove the check_warnings. The mkarg warning is raised only with -3 and check_warning fails if the error is not raised. Modified: python/branches/release26-maint/Lib/test/test_commands.py Modified: python/branches/release26-maint/Lib/test/test_commands.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_commands.py (original) +++ python/branches/release26-maint/Lib/test/test_commands.py Tue Aug 3 02:52:46 2010 @@ -58,11 +58,7 @@ /\. # and end with the name of the file. ''' - with check_warnings((".*commands.getstatus.. is deprecated", - DeprecationWarning), - ("in 3.x, mkarg has been removed", - DeprecationWarning),): - self.assertTrue(re.match(pat, getstatus("/."), re.VERBOSE)) + self.assertTrue(re.match(pat, getstatus("/."), re.VERBOSE)) def test_main(): From rdmurray at bitdance.com Tue Aug 3 03:51:11 2010 From: rdmurray at bitdance.com (R. David Murray) Date: Mon, 02 Aug 2010 21:51:11 -0400 Subject: [Python-checkins] r83543 - python/branches/py3k/Lib/test/regrtest.py In-Reply-To: <20100802185952.CF6BDEEA92@mail.python.org> References: <20100802185952.CF6BDEEA92@mail.python.org> Message-ID: <20100803015111.DC2E01F451D@kimball.webabinitio.net> On Mon, 02 Aug 2010 20:59:52 +0200, georg.brandl wrote: > Author: georg.brandl > Date: Mon Aug 2 20:59:52 2010 > New Revision: 83543 > > Log: > #8560: add progress indicator to regrtest. [...] > @@ -517,6 +517,9 @@ > else: > tests = iter(selected) > > + tests = list(tests) I guess you didn't notice that just above this code is a clause that says 'if forever' which implements -F/--forever by making tests into a generator that never runs out... -- R. David Murray www.bitdance.com From python-checkins at python.org Tue Aug 3 04:20:24 2010 From: python-checkins at python.org (ezio.melotti) Date: Tue, 3 Aug 2010 04:20:24 +0200 (CEST) Subject: [Python-checkins] r83623 - python/branches/release26-maint Message-ID: <20100803022024.94D04EEA0B@mail.python.org> Author: ezio.melotti Date: Tue Aug 3 04:20:23 2010 New Revision: 83623 Log: Unblocked revisions 75407,75409-75413,75415,75419-75421 via svnmerge ........ r75407 | antoine.pitrou | 2009-10-14 20:30:52 +0300 (Wed, 14 Oct 2009) | 3 lines Fix py3k warnings in the aifc module ........ r75409 | antoine.pitrou | 2009-10-14 21:01:33 +0300 (Wed, 14 Oct 2009) | 3 lines Fix py3k warnings in bsddb ........ r75410 | antoine.pitrou | 2009-10-14 21:09:45 +0300 (Wed, 14 Oct 2009) | 3 lines Silence a py3k warning claiming to affect Lib/calendar.py ........ r75411 | antoine.pitrou | 2009-10-14 21:12:54 +0300 (Wed, 14 Oct 2009) | 3 lines Fix a py3k warning in the StringIO module (exhibited in test_codecencodings_cn) ........ r75412 | antoine.pitrou | 2009-10-14 21:27:32 +0300 (Wed, 14 Oct 2009) | 3 lines Fix py3k warnings in the socket module ........ r75413 | antoine.pitrou | 2009-10-14 21:31:05 +0300 (Wed, 14 Oct 2009) | 3 lines Fix a py3k warning in the sndhdr module (found with test_email) ........ r75415 | antoine.pitrou | 2009-10-14 21:39:46 +0300 (Wed, 14 Oct 2009) | 3 lines Silence some py3k warnings claiming to affect _pyio ........ r75419 | antoine.pitrou | 2009-10-14 21:56:11 +0300 (Wed, 14 Oct 2009) | 3 lines Silence py3k warning claiming to affect the random module ........ r75420 | antoine.pitrou | 2009-10-14 22:04:48 +0300 (Wed, 14 Oct 2009) | 3 lines Fix py3k warnings in httplib ........ r75421 | antoine.pitrou | 2009-10-14 22:09:48 +0300 (Wed, 14 Oct 2009) | 3 lines Fix py3k warnings in the uuid module ........ Modified: python/branches/release26-maint/ (props changed) From ezio.melotti at gmail.com Tue Aug 3 05:04:56 2010 From: ezio.melotti at gmail.com (Ezio Melotti) Date: Tue, 03 Aug 2010 06:04:56 +0300 Subject: [Python-checkins] r83596 - in python/branches/release26-maint: Doc/library/constants.rst In-Reply-To: <20100802214704.85C8AEEC29@mail.python.org> References: <20100802214704.85C8AEEC29@mail.python.org> Message-ID: <4C578757.1060102@gmail.com> Hi, On 03/08/2010 0.47, georg.brandl wrote: > Author: georg.brandl > Date: Mon Aug 2 23:47:02 2010 > New Revision: 83596 > > Log: > Merged revisions 83567 via svnmerge from > svn+ssh://pythondev at svn.python.org/python/branches/release27-maint > > ................ > r83567 | georg.brandl | 2010-08-02 22:32:03 +0200 (Mo, 02 Aug 2010) | 9 lines > > Merged revisions 83552 via svnmerge from > svn+ssh://pythondev at svn.python.org/python/branches/py3k > > ........ > r83552 | georg.brandl | 2010-08-02 21:36:36 +0200 (Mo, 02 Aug 2010) | 1 line > > #9438: clarify that constant names also cannot be assigned as attributes. > ........ > ................ > > > Modified: > python/branches/release26-maint/ (props changed) > python/branches/release26-maint/Doc/library/constants.rst > > Modified: python/branches/release26-maint/Doc/library/constants.rst > ============================================================================== > --- python/branches/release26-maint/Doc/library/constants.rst (original) > +++ python/branches/release26-maint/Doc/library/constants.rst Mon Aug 2 23:47:02 2010 > @@ -3,7 +3,6 @@ > > A small number of constants live in the built-in namespace. They are: > > - > .. data:: False > > The false value of the :class:`bool` type. > @@ -39,16 +38,23 @@ > > Special value used in conjunction with extended slicing syntax. > > - .. XXX Someone who understands extended slicing should fill in here. > - > > .. data:: __debug__ > > This constant is true if Python was not started with an :option:`-O` option. > - Assignments to :const:`__debug__` are illegal and raise a :exc:`SyntaxError`. > See also the :keyword:`assert` statement. > > > +.. note:: > + > + The names :data:`None` and :data:`__debug__` cannot be reassigned > + (assignments to them, even as an attribute name, raise :exc:`SyntaxError`), > + so they can be considered "true" constants. > + > + .. versionchanged:: 2.7 > + Assignments to ``__debug__`` as an attribute became illegal. this shouldn't be here. > + > + > Constants added by the :mod:`site` module > ----------------------------------------- > > _______________________________________________ > Python-checkins mailing list > Python-checkins at python.org > http://mail.python.org/mailman/listinfo/python-checkins > Best Regards, Ezio Melotti From python-checkins at python.org Tue Aug 3 05:19:01 2010 From: python-checkins at python.org (ezio.melotti) Date: Tue, 3 Aug 2010 05:19:01 +0200 (CEST) Subject: [Python-checkins] r83624 - in python/branches/release26-maint: Lib/StringIO.py Lib/aifc.py Lib/bsddb/dbobj.py Lib/bsddb/dbshelve.py Lib/bsddb/dbtables.py Lib/bsddb/dbutils.py Lib/httplib.py Lib/sndhdr.py Lib/socket.py Lib/uuid.py Message-ID: <20100803031901.44B75EEA0B@mail.python.org> Author: ezio.melotti Date: Tue Aug 3 05:19:00 2010 New Revision: 83624 Log: Merged revisions 75407,75409-75413,75415,75419-75421 via svnmerge from svn+ssh://pythondev at svn.python.org/python/trunk ........ r75407 | antoine.pitrou | 2009-10-14 20:30:52 +0300 (Wed, 14 Oct 2009) | 3 lines Fix py3k warnings in the aifc module ........ r75409 | antoine.pitrou | 2009-10-14 21:01:33 +0300 (Wed, 14 Oct 2009) | 3 lines Fix py3k warnings in bsddb ........ r75410 | antoine.pitrou | 2009-10-14 21:09:45 +0300 (Wed, 14 Oct 2009) | 3 lines Silence a py3k warning claiming to affect Lib/calendar.py ........ r75411 | antoine.pitrou | 2009-10-14 21:12:54 +0300 (Wed, 14 Oct 2009) | 3 lines Fix a py3k warning in the StringIO module (exhibited in test_codecencodings_cn) ........ r75412 | antoine.pitrou | 2009-10-14 21:27:32 +0300 (Wed, 14 Oct 2009) | 3 lines Fix py3k warnings in the socket module ........ r75413 | antoine.pitrou | 2009-10-14 21:31:05 +0300 (Wed, 14 Oct 2009) | 3 lines Fix a py3k warning in the sndhdr module (found with test_email) ........ r75415 | antoine.pitrou | 2009-10-14 21:39:46 +0300 (Wed, 14 Oct 2009) | 3 lines Silence some py3k warnings claiming to affect _pyio ........ r75419 | antoine.pitrou | 2009-10-14 21:56:11 +0300 (Wed, 14 Oct 2009) | 3 lines Silence py3k warning claiming to affect the random module ........ r75420 | antoine.pitrou | 2009-10-14 22:04:48 +0300 (Wed, 14 Oct 2009) | 3 lines Fix py3k warnings in httplib ........ r75421 | antoine.pitrou | 2009-10-14 22:09:48 +0300 (Wed, 14 Oct 2009) | 3 lines Fix py3k warnings in the uuid module ........ Modified: python/branches/release26-maint/ (props changed) python/branches/release26-maint/Lib/StringIO.py python/branches/release26-maint/Lib/aifc.py python/branches/release26-maint/Lib/bsddb/dbobj.py python/branches/release26-maint/Lib/bsddb/dbshelve.py python/branches/release26-maint/Lib/bsddb/dbtables.py python/branches/release26-maint/Lib/bsddb/dbutils.py python/branches/release26-maint/Lib/httplib.py python/branches/release26-maint/Lib/sndhdr.py python/branches/release26-maint/Lib/socket.py python/branches/release26-maint/Lib/uuid.py Modified: python/branches/release26-maint/Lib/StringIO.py ============================================================================== --- python/branches/release26-maint/Lib/StringIO.py (original) +++ python/branches/release26-maint/Lib/StringIO.py Tue Aug 3 05:19:00 2010 @@ -128,7 +128,7 @@ if self.buflist: self.buf += ''.join(self.buflist) self.buflist = [] - if n < 0: + if n is None or n < 0: newpos = self.len else: newpos = min(self.pos+n, self.len) Modified: python/branches/release26-maint/Lib/aifc.py ============================================================================== --- python/branches/release26-maint/Lib/aifc.py (original) +++ python/branches/release26-maint/Lib/aifc.py Tue Aug 3 05:19:00 2010 @@ -409,7 +409,7 @@ data = self._ssnd_chunk.read(nframes * self._framesize) if self._convert and data: data = self._convert(data) - self._soundpos = self._soundpos + len(data) / (self._nchannels * self._sampwidth) + self._soundpos = self._soundpos + len(data) // (self._nchannels * self._sampwidth) return data # @@ -420,7 +420,7 @@ import cl dummy = self._decomp.SetParam(cl.FRAME_BUFFER_SIZE, len(data) * 2) - return self._decomp.Decompress(len(data) / self._nchannels, + return self._decomp.Decompress(len(data) // self._nchannels, data) def _ulaw2lin(self, data): @@ -439,7 +439,7 @@ def _read_comm_chunk(self, chunk): self._nchannels = _read_short(chunk) self._nframes = _read_long(chunk) - self._sampwidth = (_read_short(chunk) + 7) / 8 + self._sampwidth = (_read_short(chunk) + 7) // 8 self._framerate = int(_read_float(chunk)) self._framesize = self._nchannels * self._sampwidth if self._aifc: @@ -468,7 +468,7 @@ pass else: self._convert = self._adpcm2lin - self._framesize = self._framesize / 4 + self._framesize = self._framesize // 4 return # for ULAW and ALAW try Compression Library try: @@ -478,17 +478,17 @@ try: import audioop self._convert = self._ulaw2lin - self._framesize = self._framesize / 2 + self._framesize = self._framesize // 2 return except ImportError: pass raise Error, 'cannot read compressed AIFF-C files' if self._comptype == 'ULAW': scheme = cl.G711_ULAW - self._framesize = self._framesize / 2 + self._framesize = self._framesize // 2 elif self._comptype == 'ALAW': scheme = cl.G711_ALAW - self._framesize = self._framesize / 2 + self._framesize = self._framesize // 2 else: raise Error, 'unsupported compression type' self._decomp = cl.OpenDecompressor(scheme) @@ -706,7 +706,7 @@ def writeframesraw(self, data): self._ensure_header_written(len(data)) - nframes = len(data) / (self._sampwidth * self._nchannels) + nframes = len(data) // (self._sampwidth * self._nchannels) if self._convert: data = self._convert(data) self._file.write(data) @@ -820,17 +820,17 @@ self._init_compression() self._file.write('FORM') if not self._nframes: - self._nframes = initlength / (self._nchannels * self._sampwidth) + self._nframes = initlength // (self._nchannels * self._sampwidth) self._datalength = self._nframes * self._nchannels * self._sampwidth if self._datalength & 1: self._datalength = self._datalength + 1 if self._aifc: if self._comptype in ('ULAW', 'ALAW'): - self._datalength = self._datalength / 2 + self._datalength = self._datalength // 2 if self._datalength & 1: self._datalength = self._datalength + 1 elif self._comptype == 'G722': - self._datalength = (self._datalength + 3) / 4 + self._datalength = (self._datalength + 3) // 4 if self._datalength & 1: self._datalength = self._datalength + 1 self._form_length_pos = self._file.tell() Modified: python/branches/release26-maint/Lib/bsddb/dbobj.py ============================================================================== --- python/branches/release26-maint/Lib/bsddb/dbobj.py (original) +++ python/branches/release26-maint/Lib/bsddb/dbobj.py Tue Aug 3 05:19:00 2010 @@ -42,91 +42,91 @@ class DBEnv: def __init__(self, *args, **kwargs): - self._cobj = apply(db.DBEnv, args, kwargs) + self._cobj = db.DBEnv(*args, **kwargs) def close(self, *args, **kwargs): - return apply(self._cobj.close, args, kwargs) + return self._cobj.close(*args, **kwargs) def open(self, *args, **kwargs): - return apply(self._cobj.open, args, kwargs) + return self._cobj.open(*args, **kwargs) def remove(self, *args, **kwargs): - return apply(self._cobj.remove, args, kwargs) + return self._cobj.remove(*args, **kwargs) def set_shm_key(self, *args, **kwargs): - return apply(self._cobj.set_shm_key, args, kwargs) + return self._cobj.set_shm_key(*args, **kwargs) def set_cachesize(self, *args, **kwargs): - return apply(self._cobj.set_cachesize, args, kwargs) + return self._cobj.set_cachesize(*args, **kwargs) def set_data_dir(self, *args, **kwargs): - return apply(self._cobj.set_data_dir, args, kwargs) + return self._cobj.set_data_dir(*args, **kwargs) def set_flags(self, *args, **kwargs): - return apply(self._cobj.set_flags, args, kwargs) + return self._cobj.set_flags(*args, **kwargs) def set_lg_bsize(self, *args, **kwargs): - return apply(self._cobj.set_lg_bsize, args, kwargs) + return self._cobj.set_lg_bsize(*args, **kwargs) def set_lg_dir(self, *args, **kwargs): - return apply(self._cobj.set_lg_dir, args, kwargs) + return self._cobj.set_lg_dir(*args, **kwargs) def set_lg_max(self, *args, **kwargs): - return apply(self._cobj.set_lg_max, args, kwargs) + return self._cobj.set_lg_max(*args, **kwargs) def set_lk_detect(self, *args, **kwargs): - return apply(self._cobj.set_lk_detect, args, kwargs) + return self._cobj.set_lk_detect(*args, **kwargs) if db.version() < (4,5): def set_lk_max(self, *args, **kwargs): - return apply(self._cobj.set_lk_max, args, kwargs) + return self._cobj.set_lk_max(*args, **kwargs) def set_lk_max_locks(self, *args, **kwargs): - return apply(self._cobj.set_lk_max_locks, args, kwargs) + return self._cobj.set_lk_max_locks(*args, **kwargs) def set_lk_max_lockers(self, *args, **kwargs): - return apply(self._cobj.set_lk_max_lockers, args, kwargs) + return self._cobj.set_lk_max_lockers(*args, **kwargs) def set_lk_max_objects(self, *args, **kwargs): - return apply(self._cobj.set_lk_max_objects, args, kwargs) + return self._cobj.set_lk_max_objects(*args, **kwargs) def set_mp_mmapsize(self, *args, **kwargs): - return apply(self._cobj.set_mp_mmapsize, args, kwargs) + return self._cobj.set_mp_mmapsize(*args, **kwargs) def set_timeout(self, *args, **kwargs): - return apply(self._cobj.set_timeout, args, kwargs) + return self._cobj.set_timeout(*args, **kwargs) def set_tmp_dir(self, *args, **kwargs): - return apply(self._cobj.set_tmp_dir, args, kwargs) + return self._cobj.set_tmp_dir(*args, **kwargs) def txn_begin(self, *args, **kwargs): - return apply(self._cobj.txn_begin, args, kwargs) + return self._cobj.txn_begin(*args, **kwargs) def txn_checkpoint(self, *args, **kwargs): - return apply(self._cobj.txn_checkpoint, args, kwargs) + return self._cobj.txn_checkpoint(*args, **kwargs) def txn_stat(self, *args, **kwargs): - return apply(self._cobj.txn_stat, args, kwargs) + return self._cobj.txn_stat(*args, **kwargs) def set_tx_max(self, *args, **kwargs): - return apply(self._cobj.set_tx_max, args, kwargs) + return self._cobj.set_tx_max(*args, **kwargs) def set_tx_timestamp(self, *args, **kwargs): - return apply(self._cobj.set_tx_timestamp, args, kwargs) + return self._cobj.set_tx_timestamp(*args, **kwargs) def lock_detect(self, *args, **kwargs): - return apply(self._cobj.lock_detect, args, kwargs) + return self._cobj.lock_detect(*args, **kwargs) def lock_get(self, *args, **kwargs): - return apply(self._cobj.lock_get, args, kwargs) + return self._cobj.lock_get(*args, **kwargs) def lock_id(self, *args, **kwargs): - return apply(self._cobj.lock_id, args, kwargs) + return self._cobj.lock_id(*args, **kwargs) def lock_put(self, *args, **kwargs): - return apply(self._cobj.lock_put, args, kwargs) + return self._cobj.lock_put(*args, **kwargs) def lock_stat(self, *args, **kwargs): - return apply(self._cobj.lock_stat, args, kwargs) + return self._cobj.lock_stat(*args, **kwargs) def log_archive(self, *args, **kwargs): - return apply(self._cobj.log_archive, args, kwargs) + return self._cobj.log_archive(*args, **kwargs) def set_get_returns_none(self, *args, **kwargs): - return apply(self._cobj.set_get_returns_none, args, kwargs) + return self._cobj.set_get_returns_none(*args, **kwargs) def log_stat(self, *args, **kwargs): - return apply(self._cobj.log_stat, args, kwargs) + return self._cobj.log_stat(*args, **kwargs) if db.version() >= (4,1): def dbremove(self, *args, **kwargs): - return apply(self._cobj.dbremove, args, kwargs) + return self._cobj.dbremove(*args, **kwargs) def dbrename(self, *args, **kwargs): - return apply(self._cobj.dbrename, args, kwargs) + return self._cobj.dbrename(*args, **kwargs) def set_encrypt(self, *args, **kwargs): - return apply(self._cobj.set_encrypt, args, kwargs) + return self._cobj.set_encrypt(*args, **kwargs) if db.version() >= (4,4): def lsn_reset(self, *args, **kwargs): - return apply(self._cobj.lsn_reset, args, kwargs) + return self._cobj.lsn_reset(*args, **kwargs) class DB(MutableMapping): def __init__(self, dbenv, *args, **kwargs): # give it the proper DBEnv C object that its expecting - self._cobj = apply(db.DB, (dbenv._cobj,) + args, kwargs) + self._cobj = db.DB(*((dbenv._cobj,) + args), **kwargs) # TODO are there other dict methods that need to be overridden? def __len__(self): @@ -143,126 +143,126 @@ return self._cobj.__iter__() def append(self, *args, **kwargs): - return apply(self._cobj.append, args, kwargs) + return self._cobj.append(*args, **kwargs) def associate(self, *args, **kwargs): - return apply(self._cobj.associate, args, kwargs) + return self._cobj.associate(*args, **kwargs) def close(self, *args, **kwargs): - return apply(self._cobj.close, args, kwargs) + return self._cobj.close(*args, **kwargs) def consume(self, *args, **kwargs): - return apply(self._cobj.consume, args, kwargs) + return self._cobj.consume(*args, **kwargs) def consume_wait(self, *args, **kwargs): - return apply(self._cobj.consume_wait, args, kwargs) + return self._cobj.consume_wait(*args, **kwargs) def cursor(self, *args, **kwargs): - return apply(self._cobj.cursor, args, kwargs) + return self._cobj.cursor(*args, **kwargs) def delete(self, *args, **kwargs): - return apply(self._cobj.delete, args, kwargs) + return self._cobj.delete(*args, **kwargs) def fd(self, *args, **kwargs): - return apply(self._cobj.fd, args, kwargs) + return self._cobj.fd(*args, **kwargs) def get(self, *args, **kwargs): - return apply(self._cobj.get, args, kwargs) + return self._cobj.get(*args, **kwargs) def pget(self, *args, **kwargs): - return apply(self._cobj.pget, args, kwargs) + return self._cobj.pget(*args, **kwargs) def get_both(self, *args, **kwargs): - return apply(self._cobj.get_both, args, kwargs) + return self._cobj.get_both(*args, **kwargs) def get_byteswapped(self, *args, **kwargs): - return apply(self._cobj.get_byteswapped, args, kwargs) + return self._cobj.get_byteswapped(*args, **kwargs) def get_size(self, *args, **kwargs): - return apply(self._cobj.get_size, args, kwargs) + return self._cobj.get_size(*args, **kwargs) def get_type(self, *args, **kwargs): - return apply(self._cobj.get_type, args, kwargs) + return self._cobj.get_type(*args, **kwargs) def join(self, *args, **kwargs): - return apply(self._cobj.join, args, kwargs) + return self._cobj.join(*args, **kwargs) def key_range(self, *args, **kwargs): - return apply(self._cobj.key_range, args, kwargs) + return self._cobj.key_range(*args, **kwargs) def has_key(self, *args, **kwargs): - return apply(self._cobj.has_key, args, kwargs) + return self._cobj.has_key(*args, **kwargs) def items(self, *args, **kwargs): - return apply(self._cobj.items, args, kwargs) + return self._cobj.items(*args, **kwargs) def keys(self, *args, **kwargs): - return apply(self._cobj.keys, args, kwargs) + return self._cobj.keys(*args, **kwargs) def open(self, *args, **kwargs): - return apply(self._cobj.open, args, kwargs) + return self._cobj.open(*args, **kwargs) def put(self, *args, **kwargs): - return apply(self._cobj.put, args, kwargs) + return self._cobj.put(*args, **kwargs) def remove(self, *args, **kwargs): - return apply(self._cobj.remove, args, kwargs) + return self._cobj.remove(*args, **kwargs) def rename(self, *args, **kwargs): - return apply(self._cobj.rename, args, kwargs) + return self._cobj.rename(*args, **kwargs) def set_bt_minkey(self, *args, **kwargs): - return apply(self._cobj.set_bt_minkey, args, kwargs) + return self._cobj.set_bt_minkey(*args, **kwargs) def set_bt_compare(self, *args, **kwargs): - return apply(self._cobj.set_bt_compare, args, kwargs) + return self._cobj.set_bt_compare(*args, **kwargs) def set_cachesize(self, *args, **kwargs): - return apply(self._cobj.set_cachesize, args, kwargs) + return self._cobj.set_cachesize(*args, **kwargs) def set_flags(self, *args, **kwargs): - return apply(self._cobj.set_flags, args, kwargs) + return self._cobj.set_flags(*args, **kwargs) def set_h_ffactor(self, *args, **kwargs): - return apply(self._cobj.set_h_ffactor, args, kwargs) + return self._cobj.set_h_ffactor(*args, **kwargs) def set_h_nelem(self, *args, **kwargs): - return apply(self._cobj.set_h_nelem, args, kwargs) + return self._cobj.set_h_nelem(*args, **kwargs) def set_lorder(self, *args, **kwargs): - return apply(self._cobj.set_lorder, args, kwargs) + return self._cobj.set_lorder(*args, **kwargs) def set_pagesize(self, *args, **kwargs): - return apply(self._cobj.set_pagesize, args, kwargs) + return self._cobj.set_pagesize(*args, **kwargs) def set_re_delim(self, *args, **kwargs): - return apply(self._cobj.set_re_delim, args, kwargs) + return self._cobj.set_re_delim(*args, **kwargs) def set_re_len(self, *args, **kwargs): - return apply(self._cobj.set_re_len, args, kwargs) + return self._cobj.set_re_len(*args, **kwargs) def set_re_pad(self, *args, **kwargs): - return apply(self._cobj.set_re_pad, args, kwargs) + return self._cobj.set_re_pad(*args, **kwargs) def set_re_source(self, *args, **kwargs): - return apply(self._cobj.set_re_source, args, kwargs) + return self._cobj.set_re_source(*args, **kwargs) def set_q_extentsize(self, *args, **kwargs): - return apply(self._cobj.set_q_extentsize, args, kwargs) + return self._cobj.set_q_extentsize(*args, **kwargs) def stat(self, *args, **kwargs): - return apply(self._cobj.stat, args, kwargs) + return self._cobj.stat(*args, **kwargs) def sync(self, *args, **kwargs): - return apply(self._cobj.sync, args, kwargs) + return self._cobj.sync(*args, **kwargs) def type(self, *args, **kwargs): - return apply(self._cobj.type, args, kwargs) + return self._cobj.type(*args, **kwargs) def upgrade(self, *args, **kwargs): - return apply(self._cobj.upgrade, args, kwargs) + return self._cobj.upgrade(*args, **kwargs) def values(self, *args, **kwargs): - return apply(self._cobj.values, args, kwargs) + return self._cobj.values(*args, **kwargs) def verify(self, *args, **kwargs): - return apply(self._cobj.verify, args, kwargs) + return self._cobj.verify(*args, **kwargs) def set_get_returns_none(self, *args, **kwargs): - return apply(self._cobj.set_get_returns_none, args, kwargs) + return self._cobj.set_get_returns_none(*args, **kwargs) if db.version() >= (4,1): def set_encrypt(self, *args, **kwargs): - return apply(self._cobj.set_encrypt, args, kwargs) + return self._cobj.set_encrypt(*args, **kwargs) class DBSequence: def __init__(self, *args, **kwargs): - self._cobj = apply(db.DBSequence, args, kwargs) + self._cobj = db.DBSequence(*args, **kwargs) def close(self, *args, **kwargs): - return apply(self._cobj.close, args, kwargs) + return self._cobj.close(*args, **kwargs) def get(self, *args, **kwargs): - return apply(self._cobj.get, args, kwargs) + return self._cobj.get(*args, **kwargs) def get_dbp(self, *args, **kwargs): - return apply(self._cobj.get_dbp, args, kwargs) + return self._cobj.get_dbp(*args, **kwargs) def get_key(self, *args, **kwargs): - return apply(self._cobj.get_key, args, kwargs) + return self._cobj.get_key(*args, **kwargs) def init_value(self, *args, **kwargs): - return apply(self._cobj.init_value, args, kwargs) + return self._cobj.init_value(*args, **kwargs) def open(self, *args, **kwargs): - return apply(self._cobj.open, args, kwargs) + return self._cobj.open(*args, **kwargs) def remove(self, *args, **kwargs): - return apply(self._cobj.remove, args, kwargs) + return self._cobj.remove(*args, **kwargs) def stat(self, *args, **kwargs): - return apply(self._cobj.stat, args, kwargs) + return self._cobj.stat(*args, **kwargs) def set_cachesize(self, *args, **kwargs): - return apply(self._cobj.set_cachesize, args, kwargs) + return self._cobj.set_cachesize(*args, **kwargs) def set_flags(self, *args, **kwargs): - return apply(self._cobj.set_flags, args, kwargs) + return self._cobj.set_flags(*args, **kwargs) def set_range(self, *args, **kwargs): - return apply(self._cobj.set_range, args, kwargs) + return self._cobj.set_range(*args, **kwargs) def get_cachesize(self, *args, **kwargs): - return apply(self._cobj.get_cachesize, args, kwargs) + return self._cobj.get_cachesize(*args, **kwargs) def get_flags(self, *args, **kwargs): - return apply(self._cobj.get_flags, args, kwargs) + return self._cobj.get_flags(*args, **kwargs) def get_range(self, *args, **kwargs): - return apply(self._cobj.get_range, args, kwargs) + return self._cobj.get_range(*args, **kwargs) Modified: python/branches/release26-maint/Lib/bsddb/dbshelve.py ============================================================================== --- python/branches/release26-maint/Lib/bsddb/dbshelve.py (original) +++ python/branches/release26-maint/Lib/bsddb/dbshelve.py Tue Aug 3 05:19:00 2010 @@ -225,7 +225,7 @@ # given nothing is passed to the extension module. That way # an exception can be raised if set_get_returns_none is turned # off. - data = apply(self.db.get, args, kw) + data = self.db.get(*args, **kw) try: return cPickle.loads(data) except (EOFError, TypeError, cPickle.UnpicklingError): @@ -294,7 +294,7 @@ def get(self, *args): count = len(args) # a method overloading hack method = getattr(self, 'get_%d' % count) - apply(method, args) + method(*args) def get_1(self, flags): rec = self.dbc.get(flags) Modified: python/branches/release26-maint/Lib/bsddb/dbtables.py ============================================================================== --- python/branches/release26-maint/Lib/bsddb/dbtables.py (original) +++ python/branches/release26-maint/Lib/bsddb/dbtables.py Tue Aug 3 05:19:00 2010 @@ -398,7 +398,7 @@ # column names newcolumnlist = copy.copy(oldcolumnlist) for c in columns: - if not oldcolumnhash.has_key(c): + if c not in oldcolumnhash: newcolumnlist.append(c) # store the table's new extended column list @@ -472,7 +472,7 @@ raise TableDBError, "unknown table" # check the validity of each column name - if not self.__tablecolumns.has_key(table): + if not table in self.__tablecolumns: self.__load_column_info(table) for column in rowdict.keys() : if not self.__tablecolumns[table].count(column): @@ -615,7 +615,7 @@ argument and returning a boolean. """ try: - if not self.__tablecolumns.has_key(table): + if table not in self.__tablecolumns: self.__load_column_info(table) if columns is None: columns = self.__tablecolumns[table] @@ -639,7 +639,7 @@ argument and returning a boolean. """ # check the validity of each column name - if not self.__tablecolumns.has_key(table): + if not table in self.__tablecolumns: self.__load_column_info(table) if columns is None: columns = self.tablecolumns[table] @@ -709,28 +709,24 @@ # extract the rowid from the key rowid = key[-_rowid_str_len:] - if not rejected_rowids.has_key(rowid): + if not rowid in rejected_rowids: # if no condition was specified or the condition # succeeds, add row to our match list. if not condition or condition(data): - if not matching_rowids.has_key(rowid): + if not rowid in matching_rowids: matching_rowids[rowid] = {} if savethiscolumndata: matching_rowids[rowid][column] = data else: - if matching_rowids.has_key(rowid): + if rowid in matching_rowids: del matching_rowids[rowid] rejected_rowids[rowid] = rowid key, data = cur.next() except db.DBError, dberror: - if sys.version_info[0] < 3 : - if dberror[0] != db.DB_NOTFOUND: - raise - else : - if dberror.args[0] != db.DB_NOTFOUND: - raise + if dberror.args[0] != db.DB_NOTFOUND: + raise continue cur.close() @@ -743,7 +739,7 @@ if len(columns) > 0: for rowid, rowdata in matching_rowids.items(): for column in columns: - if rowdata.has_key(column): + if column in rowdata: continue try: rowdata[column] = self.db.get( @@ -815,13 +811,10 @@ txn.commit() txn = None - if self.__tablecolumns.has_key(table): + if table in self.__tablecolumns: del self.__tablecolumns[table] except db.DBError, dberror: if txn: txn.abort() - if sys.version_info[0] < 3 : - raise TableDBError, dberror[1] - else : - raise TableDBError, dberror.args[1] + raise TableDBError(dberror.args[1]) Modified: python/branches/release26-maint/Lib/bsddb/dbutils.py ============================================================================== --- python/branches/release26-maint/Lib/bsddb/dbutils.py (original) +++ python/branches/release26-maint/Lib/bsddb/dbutils.py Tue Aug 3 05:19:00 2010 @@ -61,7 +61,7 @@ """ sleeptime = _deadlock_MinSleepTime max_retries = _kwargs.get('max_retries', -1) - if _kwargs.has_key('max_retries'): + if 'max_retries' in _kwargs: del _kwargs['max_retries'] while True: try: Modified: python/branches/release26-maint/Lib/httplib.py ============================================================================== --- python/branches/release26-maint/Lib/httplib.py (original) +++ python/branches/release26-maint/Lib/httplib.py Tue Aug 3 05:19:00 2010 @@ -707,8 +707,8 @@ if code != 200: self.close() - raise socket.error, "Tunnel connection failed: %d %s" % (code, - message.strip()) + raise socket.error("Tunnel connection failed: %d %s" % (code, + message.strip())) while True: line = response.fp.readline() if line == '\r\n': break @@ -758,7 +758,7 @@ else: self.sock.sendall(str) except socket.error, v: - if v[0] == 32: # Broken pipe + if v.args[0] == 32: # Broken pipe self.close() raise @@ -914,7 +914,7 @@ self._send_request(method, url, body, headers) except socket.error, v: # trap 'Broken pipe' if we're allowed to automatically reconnect - if v[0] != 32 or not self.auto_open: + if v.args[0] != 32 or not self.auto_open: raise # try one more time self._send_request(method, url, body, headers) Modified: python/branches/release26-maint/Lib/sndhdr.py ============================================================================== --- python/branches/release26-maint/Lib/sndhdr.py (original) +++ python/branches/release26-maint/Lib/sndhdr.py Tue Aug 3 05:19:00 2010 @@ -100,7 +100,7 @@ else: sample_bits = '?' frame_size = sample_size * nchannels - return type, rate, nchannels, data_size/frame_size, sample_bits + return type, rate, nchannels, data_size//frame_size, sample_bits tests.append(test_au) @@ -109,7 +109,7 @@ if h[65:69] != 'FSSD' or h[128:132] != 'HCOM': return None divisor = get_long_be(h[128+16:128+20]) - return 'hcom', 22050/divisor, 1, -1, 8 + return 'hcom', 22050//divisor, 1, -1, 8 tests.append(test_hcom) Modified: python/branches/release26-maint/Lib/socket.py ============================================================================== --- python/branches/release26-maint/Lib/socket.py (original) +++ python/branches/release26-maint/Lib/socket.py Tue Aug 3 05:19:00 2010 @@ -290,12 +290,16 @@ write_offset = 0 try: while write_offset < data_size: - self._sock.sendall(buffer(data, write_offset, buffer_size)) + with warnings.catch_warnings(): + if sys.py3kwarning: + warnings.filterwarnings("ignore", ".*buffer", + DeprecationWarning) + self._sock.sendall(buffer(data, write_offset, buffer_size)) write_offset += buffer_size finally: if write_offset < data_size: remainder = data[write_offset:] - del data # explicit free + del view, data # explicit free self._wbuf.append(remainder) self._wbuf_len = len(remainder) @@ -343,7 +347,7 @@ try: data = self._sock.recv(rbufsize) except error, e: - if e[0] == EINTR: + if e.args[0] == EINTR: continue raise if not data: @@ -372,7 +376,7 @@ try: data = self._sock.recv(left) except error, e: - if e[0] == EINTR: + if e.args[0] == EINTR: continue raise if not data: @@ -427,7 +431,7 @@ except error, e: # The try..except to catch EINTR was moved outside the # recv loop to avoid the per byte overhead. - if e[0] == EINTR: + if e.args[0] == EINTR: continue raise break @@ -439,7 +443,7 @@ try: data = self._sock.recv(self._rbufsize) except error, e: - if e[0] == EINTR: + if e.args[0] == EINTR: continue raise if not data: @@ -468,7 +472,7 @@ try: data = self._sock.recv(self._rbufsize) except error, e: - if e[0] == EINTR: + if e.args[0] == EINTR: continue raise if not data: Modified: python/branches/release26-maint/Lib/uuid.py ============================================================================== --- python/branches/release26-maint/Lib/uuid.py (original) +++ python/branches/release26-maint/Lib/uuid.py Tue Aug 3 05:19:00 2010 @@ -502,8 +502,8 @@ nanoseconds = int(time.time() * 1e9) # 0x01b21dd213814000 is the number of 100-ns intervals between the # UUID epoch 1582-10-15 00:00:00 and the Unix epoch 1970-01-01 00:00:00. - timestamp = int(nanoseconds/100) + 0x01b21dd213814000L - if timestamp <= _last_timestamp: + timestamp = int(nanoseconds//100) + 0x01b21dd213814000L + if _last_timestamp is not None and timestamp <= _last_timestamp: timestamp = _last_timestamp + 1 _last_timestamp = timestamp if clock_seq is None: From solipsis at pitrou.net Tue Aug 3 05:24:06 2010 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Tue, 03 Aug 2010 05:24:06 +0200 Subject: [Python-checkins] Daily py3k reference leaks (r83618): sum=0 Message-ID: py3k results for svn r83618 (hg cset 0b12ee6d2784) -------------------------------------------------- Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/py3k/refleaks/reflogvSAKQx', '-x'] From python-checkins at python.org Tue Aug 3 05:31:33 2010 From: python-checkins at python.org (ezio.melotti) Date: Tue, 3 Aug 2010 05:31:33 +0200 (CEST) Subject: [Python-checkins] r83625 - python/branches/release26-maint Message-ID: <20100803033133.1DA8EEEA64@mail.python.org> Author: ezio.melotti Date: Tue Aug 3 05:31:32 2010 New Revision: 83625 Log: Unblocked revisions 79187-79189 via svnmerge ........ r79187 | florent.xicluna | 2010-03-21 12:50:44 +0200 (Sun, 21 Mar 2010) | 2 lines Silence more py3k warnings in unittest.case. ........ r79188 | florent.xicluna | 2010-03-21 12:51:40 +0200 (Sun, 21 Mar 2010) | 2 lines Fix py3k warnings in test_decimal, using unittest.assertItemsEqual. ........ r79189 | florent.xicluna | 2010-03-21 13:03:21 +0200 (Sun, 21 Mar 2010) | 2 lines Silence some py3k SyntaxWarning using check_py3k_warnings() with "exec" statements. ........ Modified: python/branches/release26-maint/ (props changed) From python-checkins at python.org Tue Aug 3 06:08:59 2010 From: python-checkins at python.org (ezio.melotti) Date: Tue, 3 Aug 2010 06:08:59 +0200 (CEST) Subject: [Python-checkins] r83626 - in python/branches/release26-maint: Lib/test/test_compile.py Lib/test/test_complex_args.py Lib/test/test_decimal.py Message-ID: <20100803040859.68882EECAF@mail.python.org> Author: ezio.melotti Date: Tue Aug 3 06:08:59 2010 New Revision: 83626 Log: Merged revisions 79187-79189 via svnmerge from svn+ssh://pythondev at svn.python.org/python/trunk ........ r79187 | florent.xicluna | 2010-03-21 12:50:44 +0200 (Sun, 21 Mar 2010) | 2 lines Silence more py3k warnings in unittest.case. ........ r79188 | florent.xicluna | 2010-03-21 12:51:40 +0200 (Sun, 21 Mar 2010) | 2 lines Fix py3k warnings in test_decimal, using unittest.assertItemsEqual. ........ r79189 | florent.xicluna | 2010-03-21 13:03:21 +0200 (Sun, 21 Mar 2010) | 2 lines Silence some py3k SyntaxWarning using check_py3k_warnings() with "exec" statements. ........ Modified: python/branches/release26-maint/ (props changed) python/branches/release26-maint/Lib/test/test_compile.py python/branches/release26-maint/Lib/test/test_complex_args.py python/branches/release26-maint/Lib/test/test_decimal.py Modified: python/branches/release26-maint/Lib/test/test_compile.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_compile.py (original) +++ python/branches/release26-maint/Lib/test/test_compile.py Tue Aug 3 06:08:59 2010 @@ -2,6 +2,7 @@ import sys import _ast from test import test_support +import textwrap class TestSpecifics(unittest.TestCase): @@ -129,6 +130,9 @@ def test_complex_args(self): + with test_support._check_py3k_warnings( + ("tuple parameter unpacking has been removed", SyntaxWarning)): + exec textwrap.dedent(''' def comp_args((a, b)): return a,b self.assertEqual(comp_args((1, 2)), (1, 2)) @@ -146,6 +150,7 @@ return a, b, c self.assertEqual(comp_args(1, (2, 3)), (1, 2, 3)) self.assertEqual(comp_args(), (2, 3, 4)) + ''') def test_argument_order(self): try: Modified: python/branches/release26-maint/Lib/test/test_complex_args.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_complex_args.py (original) +++ python/branches/release26-maint/Lib/test/test_complex_args.py Tue Aug 3 06:08:59 2010 @@ -1,23 +1,30 @@ import unittest from test import test_support +import textwrap class ComplexArgsTestCase(unittest.TestCase): def check(self, func, expected, *args): self.assertEqual(func(*args), expected) - # These functions are tested below as lambdas too. If you add a function test, - # also add a similar lambda test. + # These functions are tested below as lambdas too. If you add a + # function test, also add a similar lambda test. + + # Functions are wrapped in "exec" statements in order to + # silence Py3k warnings. def test_func_parens_no_unpacking(self): + exec textwrap.dedent(""" def f(((((x))))): return x self.check(f, 1, 1) # Inner parens are elided, same as: f(x,) def f(((x)),): return x self.check(f, 2, 2) + """) def test_func_1(self): + exec textwrap.dedent(""" def f(((((x),)))): return x self.check(f, 3, (3,)) def f(((((x)),))): return x @@ -26,16 +33,22 @@ self.check(f, 5, (5,)) def f(((x),)): return x self.check(f, 6, (6,)) + """) def test_func_2(self): + exec textwrap.dedent(""" def f(((((x)),),)): return x self.check(f, 2, ((2,),)) + """) def test_func_3(self): + exec textwrap.dedent(""" def f((((((x)),),),)): return x self.check(f, 3, (((3,),),)) + """) def test_func_complex(self): + exec textwrap.dedent(""" def f((((((x)),),),), a, b, c): return x, a, b, c self.check(f, (3, 9, 8, 7), (((3,),),), 9, 8, 7) @@ -44,18 +57,22 @@ def f(a, b, c, ((((((x)),)),),)): return a, b, c, x self.check(f, (9, 8, 7, 3), 9, 8, 7, (((3,),),)) + """) # Duplicate the tests above, but for lambda. If you add a lambda test, # also add a similar function test above. def test_lambda_parens_no_unpacking(self): + exec textwrap.dedent(""" f = lambda (((((x))))): x self.check(f, 1, 1) # Inner parens are elided, same as: f(x,) f = lambda ((x)),: x self.check(f, 2, 2) + """) def test_lambda_1(self): + exec textwrap.dedent(""" f = lambda (((((x),)))): x self.check(f, 3, (3,)) f = lambda (((((x)),))): x @@ -64,16 +81,22 @@ self.check(f, 5, (5,)) f = lambda (((x),)): x self.check(f, 6, (6,)) + """) def test_lambda_2(self): + exec textwrap.dedent(""" f = lambda (((((x)),),)): x self.check(f, 2, ((2,),)) + """) def test_lambda_3(self): + exec textwrap.dedent(""" f = lambda ((((((x)),),),)): x self.check(f, 3, (((3,),),)) + """) def test_lambda_complex(self): + exec textwrap.dedent(""" f = lambda (((((x)),),),), a, b, c: (x, a, b, c) self.check(f, (3, 9, 8, 7), (((3,),),), 9, 8, 7) @@ -82,10 +105,14 @@ f = lambda a, b, c, ((((((x)),)),),): (a, b, c, x) self.check(f, (9, 8, 7, 3), 9, 8, 7, (((3,),),)) + """) def test_main(): - test_support.run_unittest(ComplexArgsTestCase) + with test_support._check_py3k_warnings( + ("tuple parameter unpacking has been removed", SyntaxWarning), + ("parenthesized argument names are invalid", SyntaxWarning)): + test_support.run_unittest(ComplexArgsTestCase) if __name__ == "__main__": test_main() Modified: python/branches/release26-maint/Lib/test/test_decimal.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_decimal.py (original) +++ python/branches/release26-maint/Lib/test/test_decimal.py Tue Aug 3 06:08:59 2010 @@ -32,7 +32,7 @@ from decimal import * import numbers from test.test_support import (TestSkipped, run_unittest, run_doctest, - is_resource_enabled) + is_resource_enabled, _check_py3k_warnings) import random try: import threading @@ -219,7 +219,7 @@ if skip_expected: raise TestSkipped return - for line in open(file).xreadlines(): + for line in open(file): line = line.replace('\r\n', '').replace('\n', '') #print line try: @@ -381,8 +381,9 @@ myexceptions = self.getexceptions() self.context.clear_flags() - myexceptions.sort() - theirexceptions.sort() + with _check_py3k_warnings(quiet=True): + myexceptions.sort() + theirexceptions.sort() self.assertEqual(result, ans, 'Incorrect answer for ' + s + ' -- got ' + result) @@ -637,12 +638,13 @@ ('//', '__floordiv__', '__rfloordiv__'), ('**', '__pow__', '__rpow__') ] - if 1/2 == 0: - # testing with classic division, so add __div__ - oplist.append(('/', '__div__', '__rdiv__')) - else: - # testing with -Qnew, so add __truediv__ - oplist.append(('/', '__truediv__', '__rtruediv__')) + with _check_py3k_warnings(): + if 1 / 2 == 0: + # testing with classic division, so add __div__ + oplist.append(('/', '__div__', '__rdiv__')) + else: + # testing with -Qnew, so add __truediv__ + oplist.append(('/', '__truediv__', '__rtruediv__')) for sym, lop, rop in oplist: setattr(E, lop, lambda self, other: 'str' + lop + str(other)) @@ -1095,8 +1097,9 @@ self.assertEqual(a, b) # with None - self.assertFalse(Decimal(1) < None) - self.assertTrue(Decimal(1) > None) + with _check_py3k_warnings(): + self.assertFalse(Decimal(1) < None) + self.assertTrue(Decimal(1) > None) def test_copy_and_deepcopy_methods(self): d = Decimal('43.24') @@ -1556,18 +1559,20 @@ for flag in extra_flags: if flag not in expected_flags: expected_flags.append(flag) - expected_flags.sort() + with _check_py3k_warnings(quiet=True): + expected_flags.sort() # flags we actually got new_flags = [k for k,v in context.flags.items() if v] - new_flags.sort() + with _check_py3k_warnings(quiet=True): + new_flags.sort() self.assertEqual(ans, new_ans, "operation produces different answers depending on flags set: " + "expected %s, got %s." % (ans, new_ans)) self.assertEqual(new_flags, expected_flags, - "operation raises different flags depending on flags set: " + - "expected %s, got %s" % (expected_flags, new_flags)) + "operation raises different flags depending on flags set: " + + "expected %s, got %s" % (expected_flags, new_flags)) def test_main(arith=False, verbose=None, todo_tests=None, debug=None): """ Execute the tests. From python-checkins at python.org Tue Aug 3 06:50:52 2010 From: python-checkins at python.org (ezio.melotti) Date: Tue, 3 Aug 2010 06:50:52 +0200 (CEST) Subject: [Python-checkins] r83627 - python/branches/release26-maint/Lib/test/test_builtin.py Message-ID: <20100803045052.4747AEEA35@mail.python.org> Author: ezio.melotti Date: Tue Aug 3 06:50:52 2010 New Revision: 83627 Log: Try to avoid failures on the buildbot. Modified: python/branches/release26-maint/Lib/test/test_builtin.py Modified: python/branches/release26-maint/Lib/test/test_builtin.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_builtin.py (original) +++ python/branches/release26-maint/Lib/test/test_builtin.py Tue Aug 3 06:50:52 2010 @@ -1074,11 +1074,9 @@ with check_warnings() as w: warnings.simplefilter("always") self.assertRaises(TypeError, range, 1e100, 1e101, 1e101) - self.assertEqual(w.category, DeprecationWarning) with check_warnings() as w: warnings.simplefilter("always") self.assertEqual(range(1.0), [0]) - self.assertEqual(w.category, DeprecationWarning) self.assertRaises(TypeError, range, 0, "spam") self.assertRaises(TypeError, range, 0, 42, "spam") From python-checkins at python.org Tue Aug 3 07:17:27 2010 From: python-checkins at python.org (ezio.melotti) Date: Tue, 3 Aug 2010 07:17:27 +0200 (CEST) Subject: [Python-checkins] r83628 - python/branches/release26-maint/Lib/test/test_builtin.py Message-ID: <20100803051727.42B24EEA97@mail.python.org> Author: ezio.melotti Date: Tue Aug 3 07:17:27 2010 New Revision: 83628 Log: Fix more warnings in test_builtin. Modified: python/branches/release26-maint/Lib/test/test_builtin.py Modified: python/branches/release26-maint/Lib/test/test_builtin.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_builtin.py (original) +++ python/branches/release26-maint/Lib/test/test_builtin.py Tue Aug 3 07:17:27 2010 @@ -1577,17 +1577,25 @@ data = 'The quick Brown fox Jumped over The lazy Dog'.split() self.assertRaises(TypeError, sorted, data, None, lambda x,y: 0) +def _run_unittest(*args): + with _check_py3k_warnings( + (".+ not supported in 3.x", DeprecationWarning), + (".+ is renamed to imp.reload", DeprecationWarning), + ("classic int division", DeprecationWarning)): + run_unittest(*args) + + def test_main(verbose=None): test_classes = (BuiltinTest, TestSorted) - run_unittest(*test_classes) + _run_unittest(*test_classes) # verify reference counting if verbose and hasattr(sys, "gettotalrefcount"): import gc counts = [None] * 5 for i in xrange(len(counts)): - run_unittest(*test_classes) + _run_unittest(*test_classes) gc.collect() counts[i] = sys.gettotalrefcount() print counts From python-checkins at python.org Tue Aug 3 07:47:15 2010 From: python-checkins at python.org (ezio.melotti) Date: Tue, 3 Aug 2010 07:47:15 +0200 (CEST) Subject: [Python-checkins] r83629 - python/branches/release26-maint/Lib/test/test_MimeWriter.py Message-ID: <20100803054715.21234EEA27@mail.python.org> Author: ezio.melotti Date: Tue Aug 3 07:47:14 2010 New Revision: 83629 Log: Fix deprecation warning in test_MimeWriter. Modified: python/branches/release26-maint/Lib/test/test_MimeWriter.py Modified: python/branches/release26-maint/Lib/test/test_MimeWriter.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_MimeWriter.py (original) +++ python/branches/release26-maint/Lib/test/test_MimeWriter.py Tue Aug 3 07:47:14 2010 @@ -8,12 +8,9 @@ """ import unittest, StringIO -from test.test_support import run_unittest - -import warnings -warnings.filterwarnings("ignore", "the MimeWriter module is deprecated.*", - DeprecationWarning) +from test.test_support import run_unittest, import_module +import_module("MimeWriter", deprecated=True) from MimeWriter import MimeWriter SELLER = '''\ From python-checkins at python.org Tue Aug 3 07:50:57 2010 From: python-checkins at python.org (ezio.melotti) Date: Tue, 3 Aug 2010 07:50:57 +0200 (CEST) Subject: [Python-checkins] r83630 - python/branches/release26-maint/Lib/test/test_xmlrpc.py Message-ID: <20100803055057.39133EEA27@mail.python.org> Author: ezio.melotti Date: Tue Aug 3 07:50:57 2010 New Revision: 83630 Log: Fix deprecation warning in test_xmlrpc. Modified: python/branches/release26-maint/Lib/test/test_xmlrpc.py Modified: python/branches/release26-maint/Lib/test/test_xmlrpc.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_xmlrpc.py (original) +++ python/branches/release26-maint/Lib/test/test_xmlrpc.py Tue Aug 3 07:50:57 2010 @@ -11,6 +11,7 @@ import socket import StringIO import os +from imp import reload from test import test_support try: From python-checkins at python.org Tue Aug 3 07:55:03 2010 From: python-checkins at python.org (ezio.melotti) Date: Tue, 3 Aug 2010 07:55:03 +0200 (CEST) Subject: [Python-checkins] r83631 - python/branches/release26-maint/Lib/test/test_binascii.py Message-ID: <20100803055503.ABA27EEC6C@mail.python.org> Author: ezio.melotti Date: Tue Aug 3 07:55:03 2010 New Revision: 83631 Log: Fix deprecation warning in test_binascii. Modified: python/branches/release26-maint/Lib/test/test_binascii.py Modified: python/branches/release26-maint/Lib/test/test_binascii.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_binascii.py (original) +++ python/branches/release26-maint/Lib/test/test_binascii.py Tue Aug 3 07:55:03 2010 @@ -26,10 +26,10 @@ prefixes.extend(["crc_", "rlecode_", "rledecode_"]) for prefix in prefixes: name = prefix + suffix - self.assert_(callable(getattr(binascii, name))) + self.assertTrue(hasattr(getattr(binascii, name), '__call__')) self.assertRaises(TypeError, getattr(binascii, name)) for name in ("hexlify", "unhexlify"): - self.assert_(callable(getattr(binascii, name))) + self.assertTrue(hasattr(getattr(binascii, name), '__call__')) self.assertRaises(TypeError, getattr(binascii, name)) def test_base64valid(self): From python-checkins at python.org Tue Aug 3 07:57:13 2010 From: python-checkins at python.org (ezio.melotti) Date: Tue, 3 Aug 2010 07:57:13 +0200 (CEST) Subject: [Python-checkins] r83632 - python/branches/release26-maint/Lib/test/test_univnewlines.py Message-ID: <20100803055713.DEA08EEA27@mail.python.org> Author: ezio.melotti Date: Tue Aug 3 07:57:13 2010 New Revision: 83632 Log: Fix deprecation warning in test_univnewlines.py Modified: python/branches/release26-maint/Lib/test/test_univnewlines.py Modified: python/branches/release26-maint/Lib/test/test_univnewlines.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_univnewlines.py (original) +++ python/branches/release26-maint/Lib/test/test_univnewlines.py Tue Aug 3 07:57:13 2010 @@ -81,7 +81,8 @@ def test_execfile(self): namespace = {} - execfile(test_support.TESTFN, namespace) + with test_support._check_py3k_warnings(): + execfile(test_support.TESTFN, namespace) func = namespace['line3'] self.assertEqual(func.func_code.co_firstlineno, 3) self.assertEqual(namespace['line4'], FATX) From python-checkins at python.org Tue Aug 3 08:12:19 2010 From: python-checkins at python.org (ezio.melotti) Date: Tue, 3 Aug 2010 08:12:19 +0200 (CEST) Subject: [Python-checkins] r83633 - python/branches/release26-maint Message-ID: <20100803061219.DA3B8EEA80@mail.python.org> Author: ezio.melotti Date: Tue Aug 3 08:12:19 2010 New Revision: 83633 Log: Unblocked revisions 79156 via svnmerge ........ r79156 | florent.xicluna | 2010-03-21 00:21:02 +0200 (Sun, 21 Mar 2010) | 2 lines Cleanup test_struct using check_warnings. ........ Modified: python/branches/release26-maint/ (props changed) From python-checkins at python.org Tue Aug 3 08:13:36 2010 From: python-checkins at python.org (ezio.melotti) Date: Tue, 3 Aug 2010 08:13:36 +0200 (CEST) Subject: [Python-checkins] r83634 - in python/branches/release26-maint: Lib/test/test_struct.py Message-ID: <20100803061336.09B2CD2F6@mail.python.org> Author: ezio.melotti Date: Tue Aug 3 08:13:35 2010 New Revision: 83634 Log: Merged revisions 79156 via svnmerge from svn+ssh://pythondev at svn.python.org/python/trunk ........ r79156 | florent.xicluna | 2010-03-21 00:21:02 +0200 (Sun, 21 Mar 2010) | 2 lines Cleanup test_struct using check_warnings. ........ Modified: python/branches/release26-maint/ (props changed) python/branches/release26-maint/Lib/test/test_struct.py Modified: python/branches/release26-maint/Lib/test/test_struct.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_struct.py (original) +++ python/branches/release26-maint/Lib/test/test_struct.py Tue Aug 3 08:13:35 2010 @@ -1,10 +1,11 @@ +import os import array import unittest import struct -import warnings import inspect -warnings.filterwarnings("ignore", "struct integer overflow masking is deprecated", - DeprecationWarning) +import warnings +from test.test_support import run_unittest, check_warnings, _check_py3k_warnings + from functools import wraps from test.test_support import TestFailed, verbose, run_unittest @@ -13,6 +14,7 @@ ISBIGENDIAN = sys.byteorder == "big" IS32BIT = sys.maxsize == 0x7fffffff +testmod_filename = os.path.splitext(__file__)[0] + '.py' try: import _struct except ImportError: @@ -66,8 +68,10 @@ # SF bug 1530559. struct.pack raises TypeError where it used to convert. if PY_STRUCT_FLOAT_COERCE == 2: # Test for pre-2.5 struct module - packed = struct.pack(format, number) - floored = struct.unpack(format, packed)[0] + with check_warnings((".*integer argument expected, got float", + DeprecationWarning)) as w: + packed = struct.pack(format, number) + floored = struct.unpack(format, packed)[0] self.assertEqual(floored, int(number), "did not correcly coerce float to int") return @@ -488,31 +492,24 @@ def test_issue4228(self): # Packing a long may yield either 32 or 64 bits - x = struct.pack('L', -1)[:4] + with _check_py3k_warnings(("struct integer overflow masking is deprecated", + DeprecationWarning)): + x = struct.pack('L', -1)[:4] self.assertEqual(x, '\xff'*4) - def test_unpack_from(self): - test_string = 'abcd01234' + def test_unpack_from(self, cls=str): + data = cls('abcd01234') fmt = '4s' s = struct.Struct(fmt) - for cls in (str, buffer): - data = cls(test_string) - self.assertEqual(s.unpack_from(data), ('abcd',)) - self.assertEqual(s.unpack_from(data, 2), ('cd01',)) - self.assertEqual(s.unpack_from(data, 4), ('0123',)) - for i in xrange(6): - self.assertEqual(s.unpack_from(data, i), (data[i:i+4],)) - for i in xrange(6, len(test_string) + 1): - self.assertRaises(struct.error, s.unpack_from, data, i) - for cls in (str, buffer): - data = cls(test_string) - self.assertEqual(struct.unpack_from(fmt, data), ('abcd',)) - self.assertEqual(struct.unpack_from(fmt, data, 2), ('cd01',)) - self.assertEqual(struct.unpack_from(fmt, data, 4), ('0123',)) - for i in xrange(6): - self.assertEqual(struct.unpack_from(fmt, data, i), (data[i:i+4],)) - for i in xrange(6, len(test_string) + 1): - self.assertRaises(struct.error, struct.unpack_from, fmt, data, i) + + self.assertEqual(s.unpack_from(data), ('abcd',)) + self.assertEqual(struct.unpack_from(fmt, data), ('abcd',)) + for i in xrange(6): + self.assertEqual(s.unpack_from(data, i), (data[i:i+4],)) + self.assertEqual(struct.unpack_from(fmt, data, i), (data[i:i+4],)) + for i in xrange(6, len(data) + 1): + self.assertRaises(struct.error, s.unpack_from, data, i) + self.assertRaises(struct.error, struct.unpack_from, fmt, data, i) def test_pack_into(self): test_string = 'Reykjavik rocks, eow!' @@ -561,17 +558,21 @@ self.assertRaises(struct.error, pack_into, small_buf, 2, test_string) def test_unpack_with_buffer(self): - # SF bug 1563759: struct.unpack doens't support buffer protocol objects - data1 = array.array('B', '\x12\x34\x56\x78') - data2 = buffer('......\x12\x34\x56\x78......', 6, 4) - for data in [data1, data2]: - value, = struct.unpack('>I', data) - self.assertEqual(value, 0x12345678) + with _check_py3k_warnings(("buffer.. not supported in 3.x", + DeprecationWarning)): + # SF bug 1563759: struct.unpack doesn't support buffer protocol objects + data1 = array.array('B', '\x12\x34\x56\x78') + data2 = buffer('......\x12\x34\x56\x78......', 6, 4) + for data in [data1, data2]: + value, = struct.unpack('>I', data) + self.assertEqual(value, 0x12345678) + + self.test_unpack_from(cls=buffer) def test_bool(self): for prefix in tuple("<>!=")+('',): false = (), [], [], '', 0 - true = [1], 'test', 5, -1, 0xffffffffL+1, 0xffffffff/2 + true = [1], 'test', 5, -1, 0xffffffffL+1, 0xffffffff//2 falseFormat = prefix + '?' * len(false) packedFalse = struct.pack(falseFormat, *false) From python-checkins at python.org Tue Aug 3 08:37:50 2010 From: python-checkins at python.org (ezio.melotti) Date: Tue, 3 Aug 2010 08:37:50 +0200 (CEST) Subject: [Python-checkins] r83635 - python/branches/release26-maint/Lib/test/test_scope.py Message-ID: <20100803063750.58027EEC97@mail.python.org> Author: ezio.melotti Date: Tue Aug 3 08:37:50 2010 New Revision: 83635 Log: Fix deprecation warnings in test_scope.py Modified: python/branches/release26-maint/Lib/test/test_scope.py Modified: python/branches/release26-maint/Lib/test/test_scope.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_scope.py (original) +++ python/branches/release26-maint/Lib/test/test_scope.py Tue Aug 3 08:37:50 2010 @@ -1,9 +1,7 @@ import unittest -from test.test_support import check_syntax_error, run_unittest +from test.test_support import (check_syntax_error, _check_py3k_warnings, + check_warnings, run_unittest) -import warnings -warnings.filterwarnings("ignore", r"import \*", SyntaxWarning, "") -warnings.filterwarnings("ignore", r"import \*", SyntaxWarning, "") class ScopeTests(unittest.TestCase): @@ -332,11 +330,14 @@ self.assertEqual(makeReturner2(a=11)()['a'], 11) - def makeAddPair((a, b)): - def addPair((c, d)): - return (a + c, b + d) - return addPair - + with _check_py3k_warnings(("tuple parameter unpacking has been removed", + SyntaxWarning)): + exec """\ +def makeAddPair((a, b)): + def addPair((c, d)): + return (a + c, b + d) + return addPair +""" in locals() self.assertEqual(makeAddPair((1, 2))((100, 200)), (101,202)) def testScopeOfGlobalStmt(self): @@ -482,7 +483,7 @@ return g d = f(2)(4) - self.assert_(d.has_key('h')) + self.assertTrue('h' in d) del d['h'] self.assertEqual(d, {'x': 2, 'y': 7, 'w': 6}) @@ -659,7 +660,9 @@ def test_main(): - run_unittest(ScopeTests) + with check_warnings(("import \* only allowed at module level", + SyntaxWarning)): + run_unittest(ScopeTests) if __name__ == '__main__': test_main() From python-checkins at python.org Tue Aug 3 08:39:33 2010 From: python-checkins at python.org (richard.jones) Date: Tue, 3 Aug 2010 08:39:33 +0200 (CEST) Subject: [Python-checkins] r83636 - in python/branches/py3k/Lib/test: mock_socket.py test_smtpd.py test_smtplib.py Message-ID: <20100803063933.4C418EECDA@mail.python.org> Author: richard.jones Date: Tue Aug 3 08:39:33 2010 New Revision: 83636 Log: improvements to test_smtplib per issue2423 merged the socket mock introduced in test_smtpd Added: python/branches/py3k/Lib/test/mock_socket.py Modified: python/branches/py3k/Lib/test/test_smtpd.py python/branches/py3k/Lib/test/test_smtplib.py Added: python/branches/py3k/Lib/test/mock_socket.py ============================================================================== --- (empty file) +++ python/branches/py3k/Lib/test/mock_socket.py Tue Aug 3 08:39:33 2010 @@ -0,0 +1,153 @@ +"""Mock socket module used by the smtpd and smtplib tests. +""" + +# imported for _GLOBAL_DEFAULT_TIMEOUT +import socket as socket_module + +# Mock socket module +_defaulttimeout = None +_reply_data = None + +# This is used to queue up data to be read through socket.makefile, typically +# *before* the socket object is even created. It is intended to handle a single +# line which the socket will feed on recv() or makefile(). +def reply_with(line): + global _reply_data + _reply_data = line + + +class MockFile: + """Mock file object returned by MockSocket.makefile(). + """ + def __init__(self, lines): + self.lines = lines + def readline(self): + return self.lines.pop(0) + b'\r\n' + def close(self): + pass + + +class MockSocket: + """Mock socket object used by smtpd and smtplib tests. + """ + def __init__(self): + global _reply_data + self.output = [] + self.lines = [] + if _reply_data: + self.lines.append(_reply_data) + self.conn = None + self.timeout = None + + def queue_recv(self, line): + self.lines.append(line) + + def recv(self, bufsize, flags=None): + data = self.lines.pop(0) + b'\r\n' + return data + + def fileno(self): + return 0 + + def settimeout(self, timeout): + if timeout is None: + self.timeout = _defaulttimeout + else: + self.timeout = timeout + + def gettimeout(self): + return self.timeout + + def setsockopt(self, level, optname, value): + pass + + def getsockopt(self, level, optname, buflen=None): + return 0 + + def bind(self, address): + pass + + def accept(self): + self.conn = MockSocket() + return self.conn, 'c' + + def getsockname(self): + return ('0.0.0.0', 0) + + def setblocking(self, flag): + pass + + def listen(self, backlog): + pass + + def makefile(self, mode='r', bufsize=-1): + handle = MockFile(self.lines) + return handle + + def sendall(self, buffer, flags=None): + self.last = data + self.output.append(data) + return len(data) + + def send(self, data, flags=None): + self.last = data + self.output.append(data) + return len(data) + + def getpeername(self): + return 'peer' + + def close(self): + pass + + +def socket(family=None, type=None, proto=None): + return MockSocket() + + +def create_connection(address, timeout=socket_module._GLOBAL_DEFAULT_TIMEOUT): + try: + int_port = int(address[1]) + except ValueError: + raise error + ms = MockSocket() + if timeout is socket_module._GLOBAL_DEFAULT_TIMEOUT: + timeout = getdefaulttimeout() + ms.settimeout(timeout) + return ms + + +def setdefaulttimeout(timeout): + global _defaulttimeout + _defaulttimeout = timeout + + +def getdefaulttimeout(): + return _defaulttimeout + + +def getfqdn(): + return "" + + +def gethostname(): + pass + + +def gethostbyname(name): + return "" + + +class gaierror(Exception): + pass + + +class error(Exception): + pass + + +# Constants +AF_INET = None +SOCK_STREAM = None +SOL_SOCKET = None +SO_REUSEADDR = None Modified: python/branches/py3k/Lib/test/test_smtpd.py ============================================================================== --- python/branches/py3k/Lib/test/test_smtpd.py (original) +++ python/branches/py3k/Lib/test/test_smtpd.py Tue Aug 3 08:39:33 2010 @@ -1,53 +1,16 @@ -import asynchat from unittest import TestCase +from test import support, mock_socket import socket -from test import support -import asyncore import io import smtpd +import asyncore -# mock-ish socket to sit underneath asyncore -class DummySocket: - def __init__(self): - self.output = [] - self.queue = [] - self.conn = None - def queue_recv(self, line): - self.queue.append(line) - def recv(self, *args): - data = self.queue.pop(0) + b'\r\n' - return data - def fileno(self): - return 0 - def setsockopt(self, *args): - pass - def getsockopt(self, *args): - return 0 - def bind(self, *args): - pass - def accept(self): - self.conn = DummySocket() - return self.conn, 'c' - def listen(self, *args): - pass - def setblocking(self, *args): - pass - def send(self, data): - self.last = data - self.output.append(data) - return len(data) - def getpeername(self): - return 'peer' - def close(self): - pass class DummyServer(smtpd.SMTPServer): def __init__(self, *args): smtpd.SMTPServer.__init__(self, *args) self.messages = [] - def create_socket(self, family, type): - self.family_and_type = (socket.AF_INET, socket.SOCK_STREAM) - self.set_socket(DummySocket()) + def process_message(self, peer, mailfrom, rcpttos, data): self.messages.append((peer, mailfrom, rcpttos, data)) if data == 'return status': @@ -62,11 +25,15 @@ class SMTPDChannelTest(TestCase): def setUp(self): + smtpd.socket = asyncore.socket = mock_socket self.debug = smtpd.DEBUGSTREAM = io.StringIO() self.server = DummyServer('a', 'b') conn, addr = self.server.accept() self.channel = smtpd.SMTPChannel(self.server, conn, addr) + def tearDown(self): + asyncore.socket = smtpd.socket = socket + def write_line(self, line): self.channel.socket.queue_recv(line) self.channel.handle_read() @@ -88,7 +55,7 @@ b'502 Error: command "EHLO" not implemented\r\n') def test_HELO(self): - name = socket.getfqdn() + name = smtpd.socket.getfqdn() self.write_line(b'HELO test.example') self.assertEqual(self.channel.socket.last, '250 {}\r\n'.format(name).encode('ascii')) Modified: python/branches/py3k/Lib/test/test_smtplib.py ============================================================================== --- python/branches/py3k/Lib/test/test_smtplib.py (original) +++ python/branches/py3k/Lib/test/test_smtplib.py Tue Aug 3 08:39:33 2010 @@ -9,7 +9,7 @@ import select import unittest -from test import support +from test import support, mock_socket try: import threading @@ -48,27 +48,17 @@ serv.close() evt.set() - at unittest.skipUnless(threading, 'Threading required for this test.') class GeneralTests(unittest.TestCase): def setUp(self): - self._threads = support.threading_setup() - self.evt = threading.Event() - self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - self.sock.settimeout(15) - self.port = support.bind_port(self.sock) - servargs = (self.evt, b"220 Hola mundo\n", self.sock) - self.thread = threading.Thread(target=server, args=servargs) - self.thread.start() - self.evt.wait() - self.evt.clear() + smtplib.socket = mock_socket + self.port = 25 def tearDown(self): - self.evt.wait() - self.thread.join() - support.threading_cleanup(*self._threads) + smtplib.socket = socket def testBasic1(self): + mock_socket.reply_with(b"220 Hola mundo") # connects smtp = smtplib.SMTP(HOST, self.port) smtp.close() @@ -85,12 +75,13 @@ smtp.close() def testTimeoutDefault(self): - self.assertTrue(socket.getdefaulttimeout() is None) - socket.setdefaulttimeout(30) + self.assertTrue(mock_socket.getdefaulttimeout() is None) + mock_socket.setdefaulttimeout(30) + self.assertEqual(mock_socket.getdefaulttimeout(), 30) try: smtp = smtplib.SMTP(HOST, self.port) finally: - socket.setdefaulttimeout(None) + mock_socket.setdefaulttimeout(None) self.assertEqual(smtp.sock.gettimeout(), 30) smtp.close() @@ -155,6 +146,8 @@ class DebuggingServerTests(unittest.TestCase): def setUp(self): + self.real_getfqdn = socket.getfqdn + socket.getfqdn = mock_socket.getfqdn # temporarily replace sys.stdout to capture DebuggingServer output self.old_stdout = sys.stdout self.output = io.StringIO() @@ -176,6 +169,7 @@ self.serv_evt.clear() def tearDown(self): + socket.getfqdn = self.real_getfqdn # indicate that the client is finished self.client_evt.set() # wait for the server thread to terminate @@ -251,6 +245,12 @@ class NonConnectingTests(unittest.TestCase): + def setUp(self): + smtplib.socket = mock_socket + + def tearDown(self): + smtplib.socket = socket + def testNotConnected(self): # Test various operations on an unconnected SMTP object that # should raise exceptions (at present the attempt in SMTP.send @@ -263,9 +263,9 @@ def testNonnumericPort(self): # check that non-numeric port raises socket.error - self.assertRaises(socket.error, smtplib.SMTP, + self.assertRaises(mock_socket.error, smtplib.SMTP, "localhost", "bogus") - self.assertRaises(socket.error, smtplib.SMTP, + self.assertRaises(mock_socket.error, smtplib.SMTP, "localhost:bogus") @@ -274,25 +274,15 @@ class BadHELOServerTests(unittest.TestCase): def setUp(self): + smtplib.socket = mock_socket + mock_socket.reply_with(b"199 no hello for you!") self.old_stdout = sys.stdout self.output = io.StringIO() sys.stdout = self.output - - self._threads = support.threading_setup() - self.evt = threading.Event() - self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - self.sock.settimeout(15) - self.port = support.bind_port(self.sock) - servargs = (self.evt, b"199 no hello for you!\n", self.sock) - self.thread = threading.Thread(target=server, args=servargs) - self.thread.start() - self.evt.wait() - self.evt.clear() + self.port = 25 def tearDown(self): - self.evt.wait() - self.thread.join() - support.threading_cleanup(*self._threads) + smtplib.socket = socket sys.stdout = self.old_stdout def testFailingHELO(self): @@ -405,6 +395,8 @@ class SMTPSimTests(unittest.TestCase): def setUp(self): + self.real_getfqdn = socket.getfqdn + socket.getfqdn = mock_socket.getfqdn self._threads = support.threading_setup() self.serv_evt = threading.Event() self.client_evt = threading.Event() @@ -421,6 +413,7 @@ self.serv_evt.clear() def tearDown(self): + socket.getfqdn = self.real_getfqdn # indicate that the client is finished self.client_evt.set() # wait for the server thread to terminate From python-checkins at python.org Tue Aug 3 08:39:49 2010 From: python-checkins at python.org (ezio.melotti) Date: Tue, 3 Aug 2010 08:39:49 +0200 (CEST) Subject: [Python-checkins] r83637 - python/branches/release26-maint/Lib/test/test_struct.py Message-ID: <20100803063949.81FCEEECCC@mail.python.org> Author: ezio.melotti Date: Tue Aug 3 08:39:49 2010 New Revision: 83637 Log: Try to fix test_struct.py on some buildbots. Modified: python/branches/release26-maint/Lib/test/test_struct.py Modified: python/branches/release26-maint/Lib/test/test_struct.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_struct.py (original) +++ python/branches/release26-maint/Lib/test/test_struct.py Tue Aug 3 08:39:49 2010 @@ -493,7 +493,7 @@ def test_issue4228(self): # Packing a long may yield either 32 or 64 bits with _check_py3k_warnings(("struct integer overflow masking is deprecated", - DeprecationWarning)): + DeprecationWarning), quite=True): x = struct.pack('L', -1)[:4] self.assertEqual(x, '\xff'*4) From python-checkins at python.org Tue Aug 3 08:49:14 2010 From: python-checkins at python.org (ezio.melotti) Date: Tue, 3 Aug 2010 08:49:14 +0200 (CEST) Subject: [Python-checkins] r83638 - python/branches/release26-maint/Lib/test/test_os.py Message-ID: <20100803064914.CDEA6EE9DA@mail.python.org> Author: ezio.melotti Date: Tue Aug 3 08:49:14 2010 New Revision: 83638 Log: Fix deprecation warnings in test_os.py Modified: python/branches/release26-maint/Lib/test/test_os.py Modified: python/branches/release26-maint/Lib/test/test_os.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_os.py (original) +++ python/branches/release26-maint/Lib/test/test_os.py Tue Aug 3 08:49:14 2010 @@ -500,14 +500,16 @@ class URandomTests (unittest.TestCase): def test_urandom(self): try: - self.assertEqual(len(os.urandom(1)), 1) - self.assertEqual(len(os.urandom(10)), 10) - self.assertEqual(len(os.urandom(100)), 100) - self.assertEqual(len(os.urandom(1000)), 1000) - # see http://bugs.python.org/issue3708 - self.assertEqual(len(os.urandom(0.9)), 0) - self.assertEqual(len(os.urandom(1.1)), 1) - self.assertEqual(len(os.urandom(2.0)), 2) + with test_support._check_py3k_warnings( + ('integer argument expected, got float', DeprecationWarning)): + self.assertEqual(len(os.urandom(1)), 1) + self.assertEqual(len(os.urandom(10)), 10) + self.assertEqual(len(os.urandom(100)), 100) + self.assertEqual(len(os.urandom(1000)), 1000) + # see http://bugs.python.org/issue3708 + self.assertEqual(len(os.urandom(0.9)), 0) + self.assertEqual(len(os.urandom(1.1)), 1) + self.assertEqual(len(os.urandom(2.0)), 2) except NotImplementedError: pass From python-checkins at python.org Tue Aug 3 09:01:05 2010 From: python-checkins at python.org (ezio.melotti) Date: Tue, 3 Aug 2010 09:01:05 +0200 (CEST) Subject: [Python-checkins] r83639 - python/branches/release26-maint/Lib/test/test_long_future.py Message-ID: <20100803070105.41D74EE9DA@mail.python.org> Author: ezio.melotti Date: Tue Aug 3 09:01:05 2010 New Revision: 83639 Log: Fix deprecation warnings in test_long_future.py Modified: python/branches/release26-maint/Lib/test/test_long_future.py Modified: python/branches/release26-maint/Lib/test/test_long_future.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_long_future.py (original) +++ python/branches/release26-maint/Lib/test/test_long_future.py Tue Aug 3 09:01:05 2010 @@ -4,7 +4,7 @@ # trick just part of test_long into using future division. import unittest -from test.test_support import run_unittest +from test.test_support import run_unittest, _check_py3k_warnings class TrueDivisionTests(unittest.TestCase): def test(self): @@ -44,8 +44,9 @@ self.assertEqual(result, 0.0, "expected underflow to 0 from %r" % underflow) - for zero in ["huge / 0", "huge / 0L", "mhuge / 0", "mhuge / 0L"]: - self.assertRaises(ZeroDivisionError, eval, zero, namespace) + with _check_py3k_warnings(('classic long division', DeprecationWarning)): + for zero in ["huge / 0", "huge / 0L", "mhuge / 0", "mhuge / 0L"]: + self.assertRaises(ZeroDivisionError, eval, zero, namespace) def test_main(): From python-checkins at python.org Tue Aug 3 09:03:40 2010 From: python-checkins at python.org (ezio.melotti) Date: Tue, 3 Aug 2010 09:03:40 +0200 (CEST) Subject: [Python-checkins] r83640 - python/branches/release26-maint/Lib/hotshot/log.py Message-ID: <20100803070340.D8C48EE9DA@mail.python.org> Author: ezio.melotti Date: Tue Aug 3 09:03:40 2010 New Revision: 83640 Log: Fix deprecation warnings in test_hotshot.py Modified: python/branches/release26-maint/Lib/hotshot/log.py Modified: python/branches/release26-maint/Lib/hotshot/log.py ============================================================================== --- python/branches/release26-maint/Lib/hotshot/log.py (original) +++ python/branches/release26-maint/Lib/hotshot/log.py Tue Aug 3 09:03:40 2010 @@ -30,7 +30,7 @@ self._reader = _hotshot.logreader(logfn) self._nextitem = self._reader.next self._info = self._reader.info - if self._info.has_key('current-directory'): + if 'current-directory' in self._info: self.cwd = self._info['current-directory'] else: self.cwd = None From python-checkins at python.org Tue Aug 3 09:17:20 2010 From: python-checkins at python.org (ezio.melotti) Date: Tue, 3 Aug 2010 09:17:20 +0200 (CEST) Subject: [Python-checkins] r83641 - python/branches/release26-maint/Lib/test/test_file.py Message-ID: <20100803071720.7025AEEA32@mail.python.org> Author: ezio.melotti Date: Tue Aug 3 09:17:20 2010 New Revision: 83641 Log: Fix deprecation warnings in test_file.py Modified: python/branches/release26-maint/Lib/test/test_file.py Modified: python/branches/release26-maint/Lib/test/test_file.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_file.py (original) +++ python/branches/release26-maint/Lib/test/test_file.py Tue Aug 3 09:17:20 2010 @@ -34,13 +34,16 @@ def testAttributes(self): # verify expected attributes exist f = self.f - softspace = f.softspace + f.name # merely shouldn't blow up f.mode # ditto f.closed # ditto - # verify softspace is writable - f.softspace = softspace # merely shouldn't blow up + with test_support._check_py3k_warnings( + ('file.softspace not supported in 3.x', DeprecationWarning)): + softspace = f.softspace + # verify softspace is writable + f.softspace = softspace # merely shouldn't blow up # verify the others aren't for attr in 'name', 'mode', 'closed': @@ -111,7 +114,8 @@ for methodname in methods: method = getattr(self.f, methodname) # should raise on closed file - self.assertRaises(ValueError, method) + with test_support._check_py3k_warnings(quiet=True): + self.assertRaises(ValueError, method) self.assertRaises(ValueError, self.f.writelines, []) # file is closed, __exit__ shouldn't do anything @@ -218,7 +222,7 @@ try: f = open(TESTFN, bad_mode) except ValueError, msg: - if msg[0] != 0: + if msg.args[0] != 0: s = str(msg) if TESTFN in s or bad_mode not in s: self.fail("bad error message for invalid mode: %s" % s) From python-checkins at python.org Tue Aug 3 09:18:07 2010 From: python-checkins at python.org (ezio.melotti) Date: Tue, 3 Aug 2010 09:18:07 +0200 (CEST) Subject: [Python-checkins] r83642 - python/branches/release26-maint/Lib/test/test_struct.py Message-ID: <20100803071807.5CC7BEEA32@mail.python.org> Author: ezio.melotti Date: Tue Aug 3 09:18:07 2010 New Revision: 83642 Log: quiet, not quite. Modified: python/branches/release26-maint/Lib/test/test_struct.py Modified: python/branches/release26-maint/Lib/test/test_struct.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_struct.py (original) +++ python/branches/release26-maint/Lib/test/test_struct.py Tue Aug 3 09:18:07 2010 @@ -493,7 +493,7 @@ def test_issue4228(self): # Packing a long may yield either 32 or 64 bits with _check_py3k_warnings(("struct integer overflow masking is deprecated", - DeprecationWarning), quite=True): + DeprecationWarning), quiet=True): x = struct.pack('L', -1)[:4] self.assertEqual(x, '\xff'*4) From python-checkins at python.org Tue Aug 3 09:31:12 2010 From: python-checkins at python.org (ronald.oussoren) Date: Tue, 3 Aug 2010 09:31:12 +0200 (CEST) Subject: [Python-checkins] r83643 - in python/branches/release27-maint: Lib/test/test_posix.py Message-ID: <20100803073112.9BC1CEEA76@mail.python.org> Author: ronald.oussoren Date: Tue Aug 3 09:31:12 2010 New Revision: 83643 Log: Merged revisions 83431 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83431 | ronald.oussoren | 2010-08-01 21:18:13 +0200 (Sun, 01 Aug 2010) | 6 lines test_getgroups as introduced with issue7900 failed on systems where 'id -G' and posix.getgroups() returned the same information, but one of the sources contains duplicate information. Rewrite the check using sets instead of lists. ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Lib/test/test_posix.py Modified: python/branches/release27-maint/Lib/test/test_posix.py ============================================================================== --- python/branches/release27-maint/Lib/test/test_posix.py (original) +++ python/branches/release27-maint/Lib/test/test_posix.py Tue Aug 3 09:31:12 2010 @@ -371,11 +371,11 @@ if not groups: raise unittest.SkipTest("need working 'id -G'") - # The order of groups isn't important, hence the calls - # to sorted. + # 'id -G' and 'os.getgroups()' should return the same + # groups, ignoring order and duplicates. self.assertEqual( - list(sorted([int(x) for x in groups.split()])), - list(sorted(posix.getgroups()))) + set([int(x) for x in groups.split()]), + set(posix.getgroups())) class PosixGroupsTester(unittest.TestCase): From python-checkins at python.org Tue Aug 3 09:42:42 2010 From: python-checkins at python.org (ronald.oussoren) Date: Tue, 3 Aug 2010 09:42:42 +0200 (CEST) Subject: [Python-checkins] r83644 - python/branches/py3k/Lib/platform.py Message-ID: <20100803074242.C7D86EEC84@mail.python.org> Author: ronald.oussoren Date: Tue Aug 3 09:42:42 2010 New Revision: 83644 Log: Fix for issue 9455: platform.mac_ver() broken on OSX/ppc Modified: python/branches/py3k/Lib/platform.py Modified: python/branches/py3k/Lib/platform.py ============================================================================== --- python/branches/py3k/Lib/platform.py (original) +++ python/branches/py3k/Lib/platform.py Tue Aug 3 09:42:42 2010 @@ -777,7 +777,7 @@ release = pl['ProductVersion'] versioninfo=('', '', '') machine = os.uname()[4] - if machine == 'ppc': + if machine in ('ppc', 'Power Macintosh'): # for compatibility with the gestalt based code machine = 'PowerPC' From python-checkins at python.org Tue Aug 3 09:43:37 2010 From: python-checkins at python.org (ronald.oussoren) Date: Tue, 3 Aug 2010 09:43:37 +0200 (CEST) Subject: [Python-checkins] r83645 - in python/branches/release31-maint: Lib/platform.py Message-ID: <20100803074337.04B7CEEA35@mail.python.org> Author: ronald.oussoren Date: Tue Aug 3 09:43:36 2010 New Revision: 83645 Log: Merged revisions 83644 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83644 | ronald.oussoren | 2010-08-03 09:42:42 +0200 (Tue, 03 Aug 2010) | 2 lines Fix for issue 9455: platform.mac_ver() broken on OSX/ppc ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Lib/platform.py Modified: python/branches/release31-maint/Lib/platform.py ============================================================================== --- python/branches/release31-maint/Lib/platform.py (original) +++ python/branches/release31-maint/Lib/platform.py Tue Aug 3 09:43:36 2010 @@ -749,7 +749,7 @@ release = pl['ProductVersion'] versioninfo=('', '', '') machine = os.uname()[4] - if machine == 'ppc': + if machine in ('ppc', 'Power Macintosh'): # for compatibility with the gestalt based code machine = 'PowerPC' From python-checkins at python.org Tue Aug 3 09:44:35 2010 From: python-checkins at python.org (ronald.oussoren) Date: Tue, 3 Aug 2010 09:44:35 +0200 (CEST) Subject: [Python-checkins] r83646 - in python/branches/release27-maint: Lib/platform.py Message-ID: <20100803074435.67975EEC72@mail.python.org> Author: ronald.oussoren Date: Tue Aug 3 09:44:35 2010 New Revision: 83646 Log: Merged revisions 83644 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83644 | ronald.oussoren | 2010-08-03 09:42:42 +0200 (Tue, 03 Aug 2010) | 2 lines Fix for issue 9455: platform.mac_ver() broken on OSX/ppc ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Lib/platform.py Modified: python/branches/release27-maint/Lib/platform.py ============================================================================== --- python/branches/release27-maint/Lib/platform.py (original) +++ python/branches/release27-maint/Lib/platform.py Tue Aug 3 09:44:35 2010 @@ -781,7 +781,7 @@ release = pl['ProductVersion'] versioninfo=('', '', '') machine = os.uname()[4] - if machine == 'ppc': + if machine in ('ppc', 'Power Macintosh'): # for compatibility with the gestalt based code machine = 'PowerPC' From python-checkins at python.org Tue Aug 3 09:50:13 2010 From: python-checkins at python.org (ezio.melotti) Date: Tue, 3 Aug 2010 09:50:13 +0200 (CEST) Subject: [Python-checkins] r83647 - python/branches/release26-maint Message-ID: <20100803075013.94CF9EECDF@mail.python.org> Author: ezio.melotti Date: Tue Aug 3 09:50:13 2010 New Revision: 83647 Log: Unblocked revisions 79191 via svnmerge ........ r79191 | florent.xicluna | 2010-03-21 13:50:17 +0200 (Sun, 21 Mar 2010) | 3 lines No more deprecation warnings for distutils.sysconfig, following r78666. But when the "dl" module is available, it gives a py3k deprecation warning. ........ Modified: python/branches/release26-maint/ (props changed) From python-checkins at python.org Tue Aug 3 09:51:50 2010 From: python-checkins at python.org (ezio.melotti) Date: Tue, 3 Aug 2010 09:51:50 +0200 (CEST) Subject: [Python-checkins] r83648 - in python/branches/release26-maint: Lib/distutils/archive_util.py Lib/distutils/command/build_py.py Lib/distutils/dir_util.py Lib/distutils/filelist.py Lib/distutils/tests/test_build_ext.py Lib/test/test_distutils.py Message-ID: <20100803075150.C9EB0EECD8@mail.python.org> Author: ezio.melotti Date: Tue Aug 3 09:51:50 2010 New Revision: 83648 Log: Merged revisions 79191 via svnmerge from svn+ssh://pythondev at svn.python.org/python/trunk ........ r79191 | florent.xicluna | 2010-03-21 13:50:17 +0200 (Sun, 21 Mar 2010) | 3 lines No more deprecation warnings for distutils.sysconfig, following r78666. But when the "dl" module is available, it gives a py3k deprecation warning. ........ Modified: python/branches/release26-maint/ (props changed) python/branches/release26-maint/Lib/distutils/archive_util.py python/branches/release26-maint/Lib/distutils/command/build_py.py python/branches/release26-maint/Lib/distutils/dir_util.py python/branches/release26-maint/Lib/distutils/filelist.py python/branches/release26-maint/Lib/distutils/tests/test_build_ext.py python/branches/release26-maint/Lib/test/test_distutils.py Modified: python/branches/release26-maint/Lib/distutils/archive_util.py ============================================================================== --- python/branches/release26-maint/Lib/distutils/archive_util.py (original) +++ python/branches/release26-maint/Lib/distutils/archive_util.py Tue Aug 3 09:51:50 2010 @@ -160,7 +160,7 @@ func = format_info[0] for (arg,val) in format_info[1]: kwargs[arg] = val - filename = apply(func, (base_name, base_dir), kwargs) + filename = func(base_name, base_dir, **kwargs) try: filename = func(base_name, base_dir, **kwargs) Modified: python/branches/release26-maint/Lib/distutils/command/build_py.py ============================================================================== --- python/branches/release26-maint/Lib/distutils/command/build_py.py (original) +++ python/branches/release26-maint/Lib/distutils/command/build_py.py Tue Aug 3 09:51:50 2010 @@ -157,7 +157,7 @@ if not self.package_dir: if path: - return apply(os.path.join, path) + return os.path.join(*path) else: return '' else: @@ -184,7 +184,7 @@ tail.insert(0, pdir) if tail: - return apply(os.path.join, tail) + return os.path.join(*tail) else: return '' Modified: python/branches/release26-maint/Lib/distutils/dir_util.py ============================================================================== --- python/branches/release26-maint/Lib/distutils/dir_util.py (original) +++ python/branches/release26-maint/Lib/distutils/dir_util.py Tue Aug 3 09:51:50 2010 @@ -204,7 +204,7 @@ _build_cmdtuple(directory, cmdtuples) for cmd in cmdtuples: try: - apply(cmd[0], (cmd[1],)) + cmd[0](cmd[1]) # remove dir from cache if it's already there abspath = os.path.abspath(cmd[1]) if abspath in _path_created: Modified: python/branches/release26-maint/Lib/distutils/filelist.py ============================================================================== --- python/branches/release26-maint/Lib/distutils/filelist.py (original) +++ python/branches/release26-maint/Lib/distutils/filelist.py Tue Aug 3 09:51:50 2010 @@ -68,7 +68,7 @@ sortable_files.sort() self.files = [] for sort_tuple in sortable_files: - self.files.append(apply(os.path.join, sort_tuple)) + self.files.append(os.path.join(*sort_tuple)) # -- Other miscellaneous utility methods --------------------------- Modified: python/branches/release26-maint/Lib/distutils/tests/test_build_ext.py ============================================================================== --- python/branches/release26-maint/Lib/distutils/tests/test_build_ext.py (original) +++ python/branches/release26-maint/Lib/distutils/tests/test_build_ext.py Tue Aug 3 09:51:50 2010 @@ -349,6 +349,11 @@ self.assertEquals(wanted, path) def test_setuptools_compat(self): + try: + # on some platforms, it loads the deprecated "dl" module + test_support.import_module('setuptools_build_ext', deprecated=True) + except test_support.TestSkipped: + return from setuptools_build_ext import build_ext as setuptools_build_ext from setuptools_extension import Extension Modified: python/branches/release26-maint/Lib/test/test_distutils.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_distutils.py (original) +++ python/branches/release26-maint/Lib/test/test_distutils.py Tue Aug 3 09:51:50 2010 @@ -5,17 +5,13 @@ be run. """ +from test import test_support import distutils.tests -import test.test_support -import warnings def test_main(): - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", - "distutils.sysconfig.\w+ is deprecated", - DeprecationWarning) - test.test_support.run_unittest(distutils.tests.test_suite()) + test_support.run_unittest(distutils.tests.test_suite()) + test_support.reap_children() if __name__ == "__main__": From python-checkins at python.org Tue Aug 3 09:56:50 2010 From: python-checkins at python.org (georg.brandl) Date: Tue, 3 Aug 2010 09:56:50 +0200 (CEST) Subject: [Python-checkins] r83649 - python/branches/py3k/Lib/test/regrtest.py Message-ID: <20100803075650.39C96EEA27@mail.python.org> Author: georg.brandl Date: Tue Aug 3 09:56:50 2010 New Revision: 83649 Log: Fix regrtest -F. Modified: python/branches/py3k/Lib/test/regrtest.py Modified: python/branches/py3k/Lib/test/regrtest.py ============================================================================== --- python/branches/py3k/Lib/test/regrtest.py (original) +++ python/branches/py3k/Lib/test/regrtest.py Tue Aug 3 09:56:50 2010 @@ -514,12 +514,13 @@ if bad: return tests = test_forever() + test_count = '' + test_count_width = 3 else: tests = iter(selected) + test_count = '/{}'.format(len(selected)) + test_count_width = len(test_count) - 1 - tests = list(tests) - test_count = len(tests) - test_count_width = len(str(test_count)) if use_mp: try: from threading import Thread @@ -578,7 +579,7 @@ finished += 1 continue if not quiet: - print("[{1:{0}}/{2:{0}}] {3}".format( + print("[{1:{0}}{2}] {3}".format( test_count_width, test_index, test_count, test)) if stdout: print(stdout) @@ -597,7 +598,7 @@ else: for test_index, test in enumerate(tests, 1): if not quiet: - print("[{1:{0}}/{2:{0}}] {3}".format( + print("[{1:{0}}{2}] {3}".format( test_count_width, test_index, test_count, test)) sys.stdout.flush() if trace: From python-checkins at python.org Tue Aug 3 10:07:18 2010 From: python-checkins at python.org (ronald.oussoren) Date: Tue, 3 Aug 2010 10:07:18 +0200 (CEST) Subject: [Python-checkins] r83650 - in python/branches/release26-maint: Lib/test/test_posix.py Message-ID: <20100803080718.93BC6EECE9@mail.python.org> Author: ronald.oussoren Date: Tue Aug 3 10:07:18 2010 New Revision: 83650 Log: Merged revisions 83643 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/release27-maint ................ r83643 | ronald.oussoren | 2010-08-03 09:31:12 +0200 (Tue, 03 Aug 2010) | 13 lines Merged revisions 83431 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83431 | ronald.oussoren | 2010-08-01 21:18:13 +0200 (Sun, 01 Aug 2010) | 6 lines test_getgroups as introduced with issue7900 failed on systems where 'id -G' and posix.getgroups() returned the same information, but one of the sources contains duplicate information. Rewrite the check using sets instead of lists. ........ ................ Modified: python/branches/release26-maint/ (props changed) python/branches/release26-maint/Lib/test/test_posix.py Modified: python/branches/release26-maint/Lib/test/test_posix.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_posix.py (original) +++ python/branches/release26-maint/Lib/test/test_posix.py Tue Aug 3 10:07:18 2010 @@ -315,11 +315,11 @@ # This test needs 'id -G' return - # The order of groups isn't important, hence the calls - # to sorted. + # 'id -G' and 'os.getgroups()' should return the same + # groups, ignoring order and duplicates. self.assertEqual( - list(sorted([int(x) for x in groups.split()])), - list(sorted(posix.getgroups()))) + set([int(x) for x in groups.split()]), + set(posix.getgroups())) class PosixGroupsTester(unittest.TestCase): if posix.getuid() == 0 and hasattr(posix, 'getgroups') and sys.platform != 'darwin': From python-checkins at python.org Tue Aug 3 10:09:15 2010 From: python-checkins at python.org (ronald.oussoren) Date: Tue, 3 Aug 2010 10:09:15 +0200 (CEST) Subject: [Python-checkins] r83651 - in python/branches/release26-maint: Lib/platform.py Message-ID: <20100803080915.4C68FF9A2@mail.python.org> Author: ronald.oussoren Date: Tue Aug 3 10:09:15 2010 New Revision: 83651 Log: Merged revisions 83646 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/release27-maint ................ r83646 | ronald.oussoren | 2010-08-03 09:44:35 +0200 (Tue, 03 Aug 2010) | 9 lines Merged revisions 83644 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83644 | ronald.oussoren | 2010-08-03 09:42:42 +0200 (Tue, 03 Aug 2010) | 2 lines Fix for issue 9455: platform.mac_ver() broken on OSX/ppc ........ ................ Modified: python/branches/release26-maint/ (props changed) python/branches/release26-maint/Lib/platform.py Modified: python/branches/release26-maint/Lib/platform.py ============================================================================== --- python/branches/release26-maint/Lib/platform.py (original) +++ python/branches/release26-maint/Lib/platform.py Tue Aug 3 10:09:15 2010 @@ -771,7 +771,7 @@ release = pl['ProductVersion'] versioninfo=('', '', '') machine = os.uname()[4] - if machine == 'ppc': + if machine in ('ppc', 'Power Macintosh'): # for compatibility with the gestalt based code machine = 'PowerPC' From python-checkins at python.org Tue Aug 3 10:28:31 2010 From: python-checkins at python.org (ezio.melotti) Date: Tue, 3 Aug 2010 10:28:31 +0200 (CEST) Subject: [Python-checkins] r83652 - python/branches/release26-maint/Lib/test/test_cgi.py Message-ID: <20100803082831.84EC7EECED@mail.python.org> Author: ezio.melotti Date: Tue Aug 3 10:28:31 2010 New Revision: 83652 Log: Fix deprecation warnings in test_cgi.py Modified: python/branches/release26-maint/Lib/test/test_cgi.py Modified: python/branches/release26-maint/Lib/test/test_cgi.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_cgi.py (original) +++ python/branches/release26-maint/Lib/test/test_cgi.py Tue Aug 3 10:28:31 2010 @@ -1,4 +1,4 @@ -from test.test_support import run_unittest, check_warnings +from test.test_support import run_unittest, check_warnings, _check_py3k_warnings import cgi import os import sys @@ -179,7 +179,8 @@ self.assertEqual(d[k], v) for k, v in d.items(): self.assertEqual(expect[k], v) - self.assertEqual(sorted(expect.values()), sorted(d.values())) + with _check_py3k_warnings(): + self.assertEqual(sorted(expect.values()), sorted(d.values())) def test_log(self): cgi.log("Testing") From python-checkins at python.org Tue Aug 3 10:32:54 2010 From: python-checkins at python.org (ezio.melotti) Date: Tue, 3 Aug 2010 10:32:54 +0200 (CEST) Subject: [Python-checkins] r83653 - python/branches/release26-maint/Lib/test/test_commands.py Message-ID: <20100803083254.29B03EEAB0@mail.python.org> Author: ezio.melotti Date: Tue Aug 3 10:32:53 2010 New Revision: 83653 Log: Fix deprecation warnings in test_commands.py Modified: python/branches/release26-maint/Lib/test/test_commands.py Modified: python/branches/release26-maint/Lib/test/test_commands.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_commands.py (original) +++ python/branches/release26-maint/Lib/test/test_commands.py Tue Aug 3 10:32:53 2010 @@ -5,8 +5,8 @@ import unittest import os, tempfile, re -from test.test_support import run_unittest, reap_children, import_module, \ - check_warnings +from test.test_support import (run_unittest, reap_children, import_module, + check_warnings) from test.test_support import TestSkipped, run_unittest, reap_children, import_module # Silence Py3k warning @@ -57,8 +57,8 @@ [^/]* # Skip user, group, size, and date. /\. # and end with the name of the file. ''' - - self.assertTrue(re.match(pat, getstatus("/."), re.VERBOSE)) + with check_warnings(quiet=True): + self.assertTrue(re.match(pat, getstatus("/."), re.VERBOSE)) def test_main(): From python-checkins at python.org Tue Aug 3 10:33:58 2010 From: python-checkins at python.org (ezio.melotti) Date: Tue, 3 Aug 2010 10:33:58 +0200 (CEST) Subject: [Python-checkins] r83654 - python/branches/release26-maint/Lib/test/test_struct.py Message-ID: <20100803083358.76348EEA4C@mail.python.org> Author: ezio.melotti Date: Tue Aug 3 10:33:58 2010 New Revision: 83654 Log: Try again to fix the buildbots. Modified: python/branches/release26-maint/Lib/test/test_struct.py Modified: python/branches/release26-maint/Lib/test/test_struct.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_struct.py (original) +++ python/branches/release26-maint/Lib/test/test_struct.py Tue Aug 3 10:33:58 2010 @@ -492,8 +492,7 @@ def test_issue4228(self): # Packing a long may yield either 32 or 64 bits - with _check_py3k_warnings(("struct integer overflow masking is deprecated", - DeprecationWarning), quiet=True): + with check_warnings(quiet=True): x = struct.pack('L', -1)[:4] self.assertEqual(x, '\xff'*4) From python-checkins at python.org Tue Aug 3 10:41:02 2010 From: python-checkins at python.org (ezio.melotti) Date: Tue, 3 Aug 2010 10:41:02 +0200 (CEST) Subject: [Python-checkins] r83655 - python/branches/release26-maint/Lib/test/test_set.py Message-ID: <20100803084102.CE18DEEAEB@mail.python.org> Author: ezio.melotti Date: Tue Aug 3 10:41:02 2010 New Revision: 83655 Log: Fix deprecation warnings in test_set.py Modified: python/branches/release26-maint/Lib/test/test_set.py Modified: python/branches/release26-maint/Lib/test/test_set.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_set.py (original) +++ python/branches/release26-maint/Lib/test/test_set.py Tue Aug 3 10:41:02 2010 @@ -1385,8 +1385,9 @@ def test_copy(self): dup = self.set.copy() - dup_list = list(dup); dup_list.sort() - set_list = list(self.set); set_list.sort() + with test_support.check_warnings(): + dup_list = sorted(dup) + set_list = sorted(self.set) self.assertEqual(len(dup_list), len(set_list)) for i in range(len(dup_list)): self.failUnless(dup_list[i] is set_list[i]) @@ -1394,8 +1395,9 @@ def test_deep_copy(self): dup = copy.deepcopy(self.set) ##print type(dup), repr(dup) - dup_list = list(dup); dup_list.sort() - set_list = list(self.set); set_list.sort() + with test_support.check_warnings(): + dup_list = sorted(dup) + set_list = sorted(self.set) self.assertEqual(len(dup_list), len(set_list)) for i in range(len(dup_list)): self.assertEqual(dup_list[i], set_list[i]) @@ -1557,8 +1559,9 @@ def test_constructor(self): for cons in (set, frozenset): for s in ("123", "", range(1000), ('do', 1.2), xrange(2000,2200,5)): - for g in (G, I, Ig, S, L, R): - self.assertEqual(sorted(cons(g(s))), sorted(g(s))) + with test_support.check_warnings(): + for g in (G, I, Ig, S, L, R): + self.assertEqual(sorted(cons(g(s))), sorted(g(s))) self.assertRaises(TypeError, cons , X(s)) self.assertRaises(TypeError, cons , N(s)) self.assertRaises(ZeroDivisionError, cons , E(s)) @@ -1573,7 +1576,8 @@ if isinstance(expected, bool): self.assertEqual(actual, expected) else: - self.assertEqual(sorted(actual), sorted(expected)) + with test_support.check_warnings(): + self.assertEqual(sorted(actual), sorted(expected)) self.assertRaises(TypeError, meth, X(s)) self.assertRaises(TypeError, meth, N(s)) self.assertRaises(ZeroDivisionError, meth, E(s)) @@ -1587,7 +1591,8 @@ t = s.copy() getattr(s, methname)(list(g(data))) getattr(t, methname)(g(data)) - self.assertEqual(sorted(s), sorted(t)) + with test_support.check_warnings(): + self.assertEqual(sorted(s), sorted(t)) self.assertRaises(TypeError, getattr(set('january'), methname), X(data)) self.assertRaises(TypeError, getattr(set('january'), methname), N(data)) From python-checkins at python.org Tue Aug 3 10:54:36 2010 From: python-checkins at python.org (ezio.melotti) Date: Tue, 3 Aug 2010 10:54:36 +0200 (CEST) Subject: [Python-checkins] r83656 - python/branches/release26-maint/Lib/test/test_sets.py Message-ID: <20100803085436.E48E5EECEA@mail.python.org> Author: ezio.melotti Date: Tue Aug 3 10:54:36 2010 New Revision: 83656 Log: Fix deprecation warnings in test_sets.py Modified: python/branches/release26-maint/Lib/test/test_sets.py Modified: python/branches/release26-maint/Lib/test/test_sets.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_sets.py (original) +++ python/branches/release26-maint/Lib/test/test_sets.py Tue Aug 3 10:54:36 2010 @@ -691,8 +691,9 @@ def test_deep_copy(self): dup = copy.deepcopy(self.set) ##print type(dup), repr(dup) - dup_list = list(dup); dup_list.sort() - set_list = list(self.set); set_list.sort() + with test_support.check_warnings(): + dup_list = sorted(dup) + set_list = sorted(self.set) self.assertEqual(len(dup_list), len(set_list)) for i in range(len(dup_list)): self.assertEqual(dup_list[i], set_list[i]) From python-checkins at python.org Tue Aug 3 12:16:20 2010 From: python-checkins at python.org (georg.brandl) Date: Tue, 3 Aug 2010 12:16:20 +0200 (CEST) Subject: [Python-checkins] r83657 - python/branches/release26-maint/Doc/library/constants.rst Message-ID: <20100803101620.0A2A0EE9A8@mail.python.org> Author: georg.brandl Date: Tue Aug 3 12:16:19 2010 New Revision: 83657 Log: Fix 2.7ism. Modified: python/branches/release26-maint/Doc/library/constants.rst Modified: python/branches/release26-maint/Doc/library/constants.rst ============================================================================== --- python/branches/release26-maint/Doc/library/constants.rst (original) +++ python/branches/release26-maint/Doc/library/constants.rst Tue Aug 3 12:16:19 2010 @@ -42,17 +42,14 @@ .. data:: __debug__ This constant is true if Python was not started with an :option:`-O` option. - See also the :keyword:`assert` statement. + It cannot be reassigned. See also the :keyword:`assert` statement. .. note:: - The names :data:`None` and :data:`__debug__` cannot be reassigned - (assignments to them, even as an attribute name, raise :exc:`SyntaxError`), - so they can be considered "true" constants. - - .. versionchanged:: 2.7 - Assignments to ``__debug__`` as an attribute became illegal. + The name :data:`None` cannot be reassigned (assignments to it, even as an + attribute name, raise :exc:`SyntaxError`), so it can be considered a "true" + constant. Constants added by the :mod:`site` module From python-checkins at python.org Tue Aug 3 12:44:09 2010 From: python-checkins at python.org (ezio.melotti) Date: Tue, 3 Aug 2010 12:44:09 +0200 (CEST) Subject: [Python-checkins] r83658 - in python/branches/release26-maint/Lib/test: test_dl.py test_os.py Message-ID: <20100803104409.AEA70D8B0@mail.python.org> Author: ezio.melotti Date: Tue Aug 3 12:44:09 2010 New Revision: 83658 Log: Fix buildbot failure in test_os.py Modified: python/branches/release26-maint/Lib/test/test_dl.py python/branches/release26-maint/Lib/test/test_os.py Modified: python/branches/release26-maint/Lib/test/test_dl.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_dl.py (original) +++ python/branches/release26-maint/Lib/test/test_dl.py Tue Aug 3 12:44:09 2010 @@ -3,7 +3,10 @@ Roger E. Masse revised strategy by Barry Warsaw """ from test.test_support import verbose,TestSkipped, import_module +import warnings +warnings.simplefilter('always') dl = import_module('dl', deprecated=True) +warnings.resetwarnings() sharedlibs = [ ('/usr/lib/libc.so', 'getpid'), Modified: python/branches/release26-maint/Lib/test/test_os.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_os.py (original) +++ python/branches/release26-maint/Lib/test/test_os.py Tue Aug 3 12:44:09 2010 @@ -500,8 +500,7 @@ class URandomTests (unittest.TestCase): def test_urandom(self): try: - with test_support._check_py3k_warnings( - ('integer argument expected, got float', DeprecationWarning)): + with test_support.check_warnings(): self.assertEqual(len(os.urandom(1)), 1) self.assertEqual(len(os.urandom(10)), 10) self.assertEqual(len(os.urandom(100)), 100) From python-checkins at python.org Tue Aug 3 14:06:29 2010 From: python-checkins at python.org (georg.brandl) Date: Tue, 3 Aug 2010 14:06:29 +0200 (CEST) Subject: [Python-checkins] r83659 - in python/branches/py3k/Doc: c-api/init.rst howto/doanddont.rst library/bdb.rst library/doctest.rst library/email.errors.rst library/html.parser.rst library/io.rst library/linecache.rst library/mmap.rst library/parser.rst library/pyexpat.rst library/smtplib.rst library/string.rst library/sys.rst library/xml.sax.reader.rst whatsnew/2.4.rst whatsnew/3.2.rst Message-ID: <20100803120629.65ACAEE9C4@mail.python.org> Author: georg.brandl Date: Tue Aug 3 14:06:29 2010 New Revision: 83659 Log: Terminology fix: exceptions are raised, except in generator.throw(). Modified: python/branches/py3k/Doc/c-api/init.rst python/branches/py3k/Doc/howto/doanddont.rst python/branches/py3k/Doc/library/bdb.rst python/branches/py3k/Doc/library/doctest.rst python/branches/py3k/Doc/library/email.errors.rst python/branches/py3k/Doc/library/html.parser.rst python/branches/py3k/Doc/library/io.rst python/branches/py3k/Doc/library/linecache.rst python/branches/py3k/Doc/library/mmap.rst python/branches/py3k/Doc/library/parser.rst python/branches/py3k/Doc/library/pyexpat.rst python/branches/py3k/Doc/library/smtplib.rst python/branches/py3k/Doc/library/string.rst python/branches/py3k/Doc/library/sys.rst python/branches/py3k/Doc/library/xml.sax.reader.rst python/branches/py3k/Doc/whatsnew/2.4.rst python/branches/py3k/Doc/whatsnew/3.2.rst Modified: python/branches/py3k/Doc/c-api/init.rst ============================================================================== --- python/branches/py3k/Doc/c-api/init.rst (original) +++ python/branches/py3k/Doc/c-api/init.rst Tue Aug 3 14:06:29 2010 @@ -959,7 +959,7 @@ .. cvar:: int PyTrace_C_EXCEPTION The value for the *what* parameter to :ctype:`Py_tracefunc` functions when a C - function has thrown an exception. + function has raised an exception. .. cvar:: int PyTrace_C_RETURN Modified: python/branches/py3k/Doc/howto/doanddont.rst ============================================================================== --- python/branches/py3k/Doc/howto/doanddont.rst (original) +++ python/branches/py3k/Doc/howto/doanddont.rst Tue Aug 3 14:06:29 2010 @@ -154,7 +154,7 @@ Consider the case the file gets deleted between the time the call to :func:`os.path.exists` is made and the time :func:`open` is called. That means -the last line will throw an :exc:`IOError`. The same would happen if *file* +the last line will raise an :exc:`IOError`. The same would happen if *file* exists but has no read permission. Since testing this on a normal machine on existing and non-existing files make it seem bugless, that means in testing the results will seem fine, and the code will get shipped. Then an unhandled Modified: python/branches/py3k/Doc/library/bdb.rst ============================================================================== --- python/branches/py3k/Doc/library/bdb.rst (original) +++ python/branches/py3k/Doc/library/bdb.rst Tue Aug 3 14:06:29 2010 @@ -116,7 +116,7 @@ * ``"exception"``: An exception has occurred. * ``"c_call"``: A C function is about to be called. * ``"c_return"``: A C function has returned. - * ``"c_exception"``: A C function has thrown an exception. + * ``"c_exception"``: A C function has raised an exception. For the Python events, specialized functions (see below) are called. For the C events, no action is taken. Modified: python/branches/py3k/Doc/library/doctest.rst ============================================================================== --- python/branches/py3k/Doc/library/doctest.rst (original) +++ python/branches/py3k/Doc/library/doctest.rst Tue Aug 3 14:06:29 2010 @@ -1673,7 +1673,7 @@ .. exception:: DocTestFailure(test, example, got) - An exception thrown by :class:`DocTestRunner` to signal that a doctest example's + An exception raised by :class:`DocTestRunner` to signal that a doctest example's actual output did not match its expected output. The constructor arguments are used to initialize the member variables of the same names. @@ -1697,9 +1697,9 @@ .. exception:: UnexpectedException(test, example, exc_info) - An exception thrown by :class:`DocTestRunner` to signal that a doctest example - raised an unexpected exception. The constructor arguments are used to - initialize the member variables of the same names. + An exception raised by :class:`DocTestRunner` to signal that a doctest + example raised an unexpected exception. The constructor arguments are used + to initialize the member variables of the same names. :exc:`UnexpectedException` defines the following member variables: Modified: python/branches/py3k/Doc/library/email.errors.rst ============================================================================== --- python/branches/py3k/Doc/library/email.errors.rst (original) +++ python/branches/py3k/Doc/library/email.errors.rst Tue Aug 3 14:06:29 2010 @@ -17,7 +17,7 @@ .. exception:: MessageParseError() - This is the base class for exceptions thrown by the :class:`~email.parser.Parser` + This is the base class for exceptions raised by the :class:`~email.parser.Parser` class. It is derived from :exc:`MessageError`. Modified: python/branches/py3k/Doc/library/html.parser.rst ============================================================================== --- python/branches/py3k/Doc/library/html.parser.rst (original) +++ python/branches/py3k/Doc/library/html.parser.rst Tue Aug 3 14:06:29 2010 @@ -145,7 +145,7 @@ Method called when an unrecognized SGML declaration is read by the parser. The *data* parameter will be the entire contents of the declaration inside the ```` markup. It is sometimes useful to be be overridden by a - derived class; the base class implementation throws an :exc:`HTMLParseError`. + derived class; the base class implementation raises an :exc:`HTMLParseError`. .. method:: HTMLParser.handle_pi(data) Modified: python/branches/py3k/Doc/library/io.rst ============================================================================== --- python/branches/py3k/Doc/library/io.rst (original) +++ python/branches/py3k/Doc/library/io.rst Tue Aug 3 14:06:29 2010 @@ -17,7 +17,7 @@ At the top of the I/O hierarchy is the abstract base class :class:`IOBase`. It defines the basic interface to a stream. Note, however, that there is no separation between reading and writing to streams; implementations are allowed -to throw an :exc:`IOError` if they do not support a given operation. +to raise an :exc:`IOError` if they do not support a given operation. Extending :class:`IOBase` is :class:`RawIOBase` which deals simply with the reading and writing of raw bytes to a stream. :class:`FileIO` subclasses Modified: python/branches/py3k/Doc/library/linecache.rst ============================================================================== --- python/branches/py3k/Doc/library/linecache.rst (original) +++ python/branches/py3k/Doc/library/linecache.rst Tue Aug 3 14:06:29 2010 @@ -16,7 +16,7 @@ .. function:: getline(filename, lineno, module_globals=None) - Get line *lineno* from file named *filename*. This function will never throw an + Get line *lineno* from file named *filename*. This function will never raise an exception --- it will return ``''`` on errors (the terminating newline character will be included for lines that are found). Modified: python/branches/py3k/Doc/library/mmap.rst ============================================================================== --- python/branches/py3k/Doc/library/mmap.rst (original) +++ python/branches/py3k/Doc/library/mmap.rst Tue Aug 3 14:06:29 2010 @@ -184,7 +184,7 @@ Copy the *count* bytes starting at offset *src* to the destination index *dest*. If the mmap was created with :const:`ACCESS_READ`, then calls to - move will throw a :exc:`TypeError` exception. + move will raise a :exc:`TypeError` exception. .. method:: read(num) @@ -210,7 +210,7 @@ Resizes the map and the underlying file, if any. If the mmap was created with :const:`ACCESS_READ` or :const:`ACCESS_COPY`, resizing the map will - throw a :exc:`TypeError` exception. + raise a :exc:`TypeError` exception. .. method:: rfind(sub[, start[, end]]) @@ -245,7 +245,7 @@ Write the bytes in *bytes* into memory at the current position of the file pointer; the file position is updated to point after the bytes that were written. If the mmap was created with :const:`ACCESS_READ`, then - writing to it will throw a :exc:`TypeError` exception. + writing to it will raise a :exc:`TypeError` exception. .. method:: write_byte(byte) @@ -253,4 +253,4 @@ Write the the integer *byte* into memory at the current position of the file pointer; the file position is advanced by ``1``. If the mmap was created with :const:`ACCESS_READ`, then writing to it will - throw a :exc:`TypeError` exception. + raise a :exc:`TypeError` exception. Modified: python/branches/py3k/Doc/library/parser.rst ============================================================================== --- python/branches/py3k/Doc/library/parser.rst (original) +++ python/branches/py3k/Doc/library/parser.rst Tue Aug 3 14:06:29 2010 @@ -114,7 +114,7 @@ The :func:`expr` function parses the parameter *source* as if it were an input to ``compile(source, 'file.py', 'eval')``. If the parse succeeds, an ST object is created to hold the internal parse tree representation, otherwise an - appropriate exception is thrown. + appropriate exception is raised. .. function:: suite(source) @@ -122,7 +122,7 @@ The :func:`suite` function parses the parameter *source* as if it were an input to ``compile(source, 'file.py', 'exec')``. If the parse succeeds, an ST object is created to hold the internal parse tree representation, otherwise an - appropriate exception is thrown. + appropriate exception is raised. .. function:: sequence2st(sequence) @@ -132,9 +132,9 @@ to the Python grammar and all nodes are valid node types in the host version of Python, an ST object is created from the internal representation and returned to the called. If there is a problem creating the internal representation, or - if the tree cannot be validated, a :exc:`ParserError` exception is thrown. An + if the tree cannot be validated, a :exc:`ParserError` exception is raised. An ST object created this way should not be assumed to compile correctly; normal - exceptions thrown by compilation may still be initiated when the ST object is + exceptions raised by compilation may still be initiated when the ST object is passed to :func:`compilest`. This may indicate problems not related to syntax (such as a :exc:`MemoryError` exception), but may also be due to constructs such as the result of parsing ``del f(0)``, which escapes the Python parser but is @@ -259,8 +259,8 @@ .. exception:: ParserError Exception raised when a failure occurs within the parser module. This is - generally produced for validation failures rather than the built in - :exc:`SyntaxError` thrown during normal parsing. The exception argument is + generally produced for validation failures rather than the built-in + :exc:`SyntaxError` raised during normal parsing. The exception argument is either a string describing the reason of the failure or a tuple containing a sequence causing the failure from a parse tree passed to :func:`sequence2st` and an explanatory string. Calls to :func:`sequence2st` need to be able to @@ -268,7 +268,7 @@ will only need to be aware of the simple string values. Note that the functions :func:`compilest`, :func:`expr`, and :func:`suite` may -throw exceptions which are normally thrown by the parsing and compilation +raise exceptions which are normally thrown by the parsing and compilation process. These include the built in exceptions :exc:`MemoryError`, :exc:`OverflowError`, :exc:`SyntaxError`, and :exc:`SystemError`. In these cases, these exceptions carry all the meaning normally associated with them. Modified: python/branches/py3k/Doc/library/pyexpat.rst ============================================================================== --- python/branches/py3k/Doc/library/pyexpat.rst (original) +++ python/branches/py3k/Doc/library/pyexpat.rst Tue Aug 3 14:06:29 2010 @@ -429,7 +429,7 @@ Called if the XML document hasn't been declared as being a standalone document. This happens when there is an external subset or a reference to a parameter entity, but the XML declaration does not set standalone to ``yes`` in an XML - declaration. If this handler returns ``0``, then the parser will throw an + declaration. If this handler returns ``0``, then the parser will raise an :const:`XML_ERROR_NOT_STANDALONE` error. If this handler is not set, no exception is raised by the parser for this condition. @@ -446,7 +446,7 @@ responsible for creating the sub-parser using ``ExternalEntityParserCreate(context)``, initializing it with the appropriate callbacks, and parsing the entity. This handler should return an integer; if it - returns ``0``, the parser will throw an + returns ``0``, the parser will raise an :const:`XML_ERROR_EXTERNAL_ENTITY_HANDLING` error, otherwise parsing will continue. Modified: python/branches/py3k/Doc/library/smtplib.rst ============================================================================== --- python/branches/py3k/Doc/library/smtplib.rst (original) +++ python/branches/py3k/Doc/library/smtplib.rst Tue Aug 3 14:06:29 2010 @@ -284,9 +284,9 @@ and ESMTP options suppressed. This method will return normally if the mail is accepted for at least one - recipient. Otherwise it will throw an exception. That is, if this method does - not throw an exception, then someone should get your mail. If this method does - not throw an exception, it returns a dictionary, with one entry for each + recipient. Otherwise it will raise an exception. That is, if this method does + not raise an exception, then someone should get your mail. If this method does + not raise an exception, it returns a dictionary, with one entry for each recipient that was refused. Each entry contains a tuple of the SMTP error code and the accompanying error message sent by the server. Modified: python/branches/py3k/Doc/library/string.rst ============================================================================== --- python/branches/py3k/Doc/library/string.rst (original) +++ python/branches/py3k/Doc/library/string.rst Tue Aug 3 14:06:29 2010 @@ -163,7 +163,7 @@ the format string (integers for positional arguments, and strings for named arguments), and a reference to the *args* and *kwargs* that was passed to vformat. The set of unused args can be calculated from these - parameters. :meth:`check_unused_args` is assumed to throw an exception if + parameters. :meth:`check_unused_args` is assumed to raise an exception if the check fails. .. method:: format_field(value, format_spec) Modified: python/branches/py3k/Doc/library/sys.rst ============================================================================== --- python/branches/py3k/Doc/library/sys.rst (original) +++ python/branches/py3k/Doc/library/sys.rst Tue Aug 3 14:06:29 2010 @@ -843,7 +843,7 @@ A C function has returned. *arg* is ``None``. ``'c_exception'`` - A C function has thrown an exception. *arg* is ``None``. + A C function has raised an exception. *arg* is ``None``. Note that as an exception is propagated down the chain of callers, an ``'exception'`` event is generated at each level. Modified: python/branches/py3k/Doc/library/xml.sax.reader.rst ============================================================================== --- python/branches/py3k/Doc/library/xml.sax.reader.rst (original) +++ python/branches/py3k/Doc/library/xml.sax.reader.rst Tue Aug 3 14:06:29 2010 @@ -154,7 +154,7 @@ Allow an application to set the locale for errors and warnings. SAX parsers are not required to provide localization for errors and warnings; if - they cannot support the requested locale, however, they must throw a SAX + they cannot support the requested locale, however, they must raise a SAX exception. Applications may request a locale change in the middle of a parse. Modified: python/branches/py3k/Doc/whatsnew/2.4.rst ============================================================================== --- python/branches/py3k/Doc/whatsnew/2.4.rst (original) +++ python/branches/py3k/Doc/whatsnew/2.4.rst Tue Aug 3 14:06:29 2010 @@ -1066,7 +1066,7 @@ deprecated APIs and removes support for Python versions earlier than 2.3. The 3.0 version of the package uses a new incremental parser for MIME messages, available in the :mod:`email.FeedParser` module. The new parser doesn't require - reading the entire message into memory, and doesn't throw exceptions if a + reading the entire message into memory, and doesn't raise exceptions if a message is malformed; instead it records any problems in the :attr:`defect` attribute of the message. (Developed by Anthony Baxter, Barry Warsaw, Thomas Wouters, and others.) Modified: python/branches/py3k/Doc/whatsnew/3.2.rst ============================================================================== --- python/branches/py3k/Doc/whatsnew/3.2.rst (original) +++ python/branches/py3k/Doc/whatsnew/3.2.rst Tue Aug 3 14:06:29 2010 @@ -114,7 +114,7 @@ * *ignore_dangling_symlinks*: when ``symlinks=False`` (meaning that the function copies the file pointed to by the symlink, not the symlink - itself) this option will silence the error thrown if the file doesn't + itself) this option will silence the error raised if the file doesn't exist. * *copy_function*: a callable that will be used to copy files. From python-checkins at python.org Tue Aug 3 14:32:00 2010 From: python-checkins at python.org (georg.brandl) Date: Tue, 3 Aug 2010 14:32:00 +0200 (CEST) Subject: [Python-checkins] r83660 - python/branches/py3k/Doc/tools/sphinx-build.py Message-ID: <20100803123200.10416EE9AA@mail.python.org> Author: georg.brandl Date: Tue Aug 3 14:31:59 2010 New Revision: 83660 Log: Get rid of UserWarnings when running Sphinx from tools dir. Modified: python/branches/py3k/Doc/tools/sphinx-build.py Modified: python/branches/py3k/Doc/tools/sphinx-build.py ============================================================================== --- python/branches/py3k/Doc/tools/sphinx-build.py (original) +++ python/branches/py3k/Doc/tools/sphinx-build.py Tue Aug 3 14:31:59 2010 @@ -3,11 +3,15 @@ Sphinx - Python documentation toolchain ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - :copyright: 2007 by Georg Brandl. + :copyright: 2007-2010 by Georg Brandl. :license: Python license. """ import sys +import warnings + +# Get rid of UserWarnings reported by pkg_resources. +warnings.filterwarnings('ignore', category=UserWarning, module='jinja2') if __name__ == '__main__': From python-checkins at python.org Tue Aug 3 14:34:08 2010 From: python-checkins at python.org (georg.brandl) Date: Tue, 3 Aug 2010 14:34:08 +0200 (CEST) Subject: [Python-checkins] r83661 - in python/branches/release27-maint: Doc/tools/sphinx-build.py Message-ID: <20100803123408.34FACEE9AA@mail.python.org> Author: georg.brandl Date: Tue Aug 3 14:34:07 2010 New Revision: 83661 Log: Merged revisions 83660 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83660 | georg.brandl | 2010-08-03 14:31:59 +0200 (Di, 03 Aug 2010) | 1 line Get rid of UserWarnings when running Sphinx from tools dir. ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Doc/tools/sphinx-build.py Modified: python/branches/release27-maint/Doc/tools/sphinx-build.py ============================================================================== --- python/branches/release27-maint/Doc/tools/sphinx-build.py (original) +++ python/branches/release27-maint/Doc/tools/sphinx-build.py Tue Aug 3 14:34:07 2010 @@ -3,11 +3,15 @@ Sphinx - Python documentation toolchain ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - :copyright: 2007 by Georg Brandl. + :copyright: 2007-2010 by Georg Brandl. :license: Python license. """ import sys +import warnings + +# Get rid of UserWarnings reported by pkg_resources. +warnings.filterwarnings('ignore', category=UserWarning, module='jinja2') if __name__ == '__main__': From python-checkins at python.org Tue Aug 3 14:34:36 2010 From: python-checkins at python.org (georg.brandl) Date: Tue, 3 Aug 2010 14:34:36 +0200 (CEST) Subject: [Python-checkins] r83662 - in python/branches/release31-maint: Doc/tools/sphinx-build.py Message-ID: <20100803123436.12D62EE9BA@mail.python.org> Author: georg.brandl Date: Tue Aug 3 14:34:35 2010 New Revision: 83662 Log: Merged revisions 83660 via svnmerge from svn+ssh://svn.python.org/python/branches/py3k ........ r83660 | georg.brandl | 2010-08-03 14:31:59 +0200 (Di, 03 Aug 2010) | 1 line Get rid of UserWarnings when running Sphinx from tools dir. ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Doc/tools/sphinx-build.py Modified: python/branches/release31-maint/Doc/tools/sphinx-build.py ============================================================================== --- python/branches/release31-maint/Doc/tools/sphinx-build.py (original) +++ python/branches/release31-maint/Doc/tools/sphinx-build.py Tue Aug 3 14:34:35 2010 @@ -3,11 +3,15 @@ Sphinx - Python documentation toolchain ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - :copyright: 2007 by Georg Brandl. + :copyright: 2007-2010 by Georg Brandl. :license: Python license. """ import sys +import warnings + +# Get rid of UserWarnings reported by pkg_resources. +warnings.filterwarnings('ignore', category=UserWarning, module='jinja2') if __name__ == '__main__': From python-checkins at python.org Tue Aug 3 14:36:57 2010 From: python-checkins at python.org (georg.brandl) Date: Tue, 3 Aug 2010 14:36:57 +0200 (CEST) Subject: [Python-checkins] r83663 - python/branches/py3k/Doc/Makefile Message-ID: <20100803123657.E5923F3E4@mail.python.org> Author: georg.brandl Date: Tue Aug 3 14:36:57 2010 New Revision: 83663 Log: A couple of nits how to ignore errors. Modified: python/branches/py3k/Doc/Makefile Modified: python/branches/py3k/Doc/Makefile ============================================================================== --- python/branches/py3k/Doc/Makefile (original) +++ python/branches/py3k/Doc/Makefile Tue Aug 3 14:36:57 2010 @@ -120,7 +120,7 @@ -rm -rf tools/docutils dist: - -rm -rf dist + rm -rf dist mkdir -p dist # archive the HTML @@ -142,7 +142,7 @@ rm dist/python-$(DISTVERSION)-docs-text.tar # archive the A4 latex - -rm -r build/latex + rm -rf build/latex make latex PAPER=a4 -sed -i 's/makeindex/makeindex -q/' build/latex/Makefile (cd build/latex; make clean && make all-pdf && make FMT=pdf zip bz2) @@ -150,7 +150,7 @@ cp build/latex/docs-pdf.tar.bz2 dist/python-$(DISTVERSION)-docs-pdf-a4.tar.bz2 # archive the letter latex - rm -r build/latex + rm -rf build/latex make latex PAPER=letter -sed -i 's/makeindex/makeindex -q/' build/latex/Makefile (cd build/latex; make clean && make all-pdf && make FMT=pdf zip bz2) From python-checkins at python.org Tue Aug 3 14:37:20 2010 From: python-checkins at python.org (georg.brandl) Date: Tue, 3 Aug 2010 14:37:20 +0200 (CEST) Subject: [Python-checkins] r83664 - in python/branches/release27-maint: Doc/Makefile Message-ID: <20100803123720.08161F3E4@mail.python.org> Author: georg.brandl Date: Tue Aug 3 14:37:19 2010 New Revision: 83664 Log: Merged revisions 83663 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83663 | georg.brandl | 2010-08-03 14:36:57 +0200 (Di, 03 Aug 2010) | 1 line A couple of nits how to ignore errors. ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Doc/Makefile Modified: python/branches/release27-maint/Doc/Makefile ============================================================================== --- python/branches/release27-maint/Doc/Makefile (original) +++ python/branches/release27-maint/Doc/Makefile Tue Aug 3 14:37:19 2010 @@ -120,7 +120,7 @@ -rm -rf tools/docutils dist: - -rm -rf dist + rm -rf dist mkdir -p dist # archive the HTML @@ -142,7 +142,7 @@ rm dist/python-$(DISTVERSION)-docs-text.tar # archive the A4 latex - -rm -r build/latex + rm -rf build/latex make latex PAPER=a4 -sed -i 's/makeindex/makeindex -q/' build/latex/Makefile (cd build/latex; make clean && make all-pdf && make FMT=pdf zip bz2) @@ -150,7 +150,7 @@ cp build/latex/docs-pdf.tar.bz2 dist/python-$(DISTVERSION)-docs-pdf-a4.tar.bz2 # archive the letter latex - rm -r build/latex + rm -rf build/latex make latex PAPER=letter -sed -i 's/makeindex/makeindex -q/' build/latex/Makefile (cd build/latex; make clean && make all-pdf && make FMT=pdf zip bz2) From python-checkins at python.org Tue Aug 3 14:37:34 2010 From: python-checkins at python.org (georg.brandl) Date: Tue, 3 Aug 2010 14:37:34 +0200 (CEST) Subject: [Python-checkins] r83665 - in python/branches/release31-maint: Doc/Makefile Message-ID: <20100803123734.9E710EE9EA@mail.python.org> Author: georg.brandl Date: Tue Aug 3 14:37:34 2010 New Revision: 83665 Log: Merged revisions 83663 via svnmerge from svn+ssh://svn.python.org/python/branches/py3k ........ r83663 | georg.brandl | 2010-08-03 14:36:57 +0200 (Di, 03 Aug 2010) | 1 line A couple of nits how to ignore errors. ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Doc/Makefile Modified: python/branches/release31-maint/Doc/Makefile ============================================================================== --- python/branches/release31-maint/Doc/Makefile (original) +++ python/branches/release31-maint/Doc/Makefile Tue Aug 3 14:37:34 2010 @@ -119,7 +119,7 @@ -rm -rf tools/docutils dist: - -rm -rf dist + rm -rf dist mkdir -p dist # archive the HTML @@ -141,7 +141,7 @@ rm dist/python-$(DISTVERSION)-docs-text.tar # archive the A4 latex - -rm -r build/latex + rm -rf build/latex make latex PAPER=a4 -sed -i 's/makeindex/makeindex -q/' build/latex/Makefile (cd build/latex; make clean && make all-pdf && make FMT=pdf zip bz2) @@ -149,7 +149,7 @@ cp build/latex/docs-pdf.tar.bz2 dist/python-$(DISTVERSION)-docs-pdf-a4.tar.bz2 # archive the letter latex - rm -r build/latex + rm -rf build/latex make latex PAPER=letter -sed -i 's/makeindex/makeindex -q/' build/latex/Makefile (cd build/latex; make clean && make all-pdf && make FMT=pdf zip bz2) From python-checkins at python.org Tue Aug 3 14:39:02 2010 From: python-checkins at python.org (georg.brandl) Date: Tue, 3 Aug 2010 14:39:02 +0200 (CEST) Subject: [Python-checkins] r83666 - python/branches/release27-maint/Doc/tools/dailybuild.py Message-ID: <20100803123902.69909F3E4@mail.python.org> Author: georg.brandl Date: Tue Aug 3 14:39:02 2010 New Revision: 83666 Log: Update dailybuild script to newest version from dinsdale. Modified: python/branches/release27-maint/Doc/tools/dailybuild.py Modified: python/branches/release27-maint/Doc/tools/dailybuild.py ============================================================================== --- python/branches/release27-maint/Doc/tools/dailybuild.py (original) +++ python/branches/release27-maint/Doc/tools/dailybuild.py Tue Aug 3 14:39:02 2010 @@ -33,10 +33,9 @@ BRANCHES = [ # checkout, target, isdev - (BUILDROOT + '/python26', WWWROOT, False), + (BUILDROOT + '/python32', WWWROOT + '/dev', True), + (BUILDROOT + '/python27', WWWROOT, False), (BUILDROOT + '/python31', WWWROOT + '/py3k', False), - (BUILDROOT + '/python27', WWWROOT + '/dev', True), - (BUILDROOT + '/python32', WWWROOT + '/dev/py3k', True), ] @@ -53,7 +52,7 @@ print 'Copying HTML files' os.system('cp -a Doc/build/html/* %s' % target) print 'Copying dist files' - os.system('mkdir %s/archives' % target) + os.system('mkdir -p %s/archives' % target) os.system('cp -a Doc/dist/* %s/archives' % target) print 'Finished' print '=' * 80 From g.brandl at gmx.net Tue Aug 3 15:10:03 2010 From: g.brandl at gmx.net (Georg Brandl) Date: Tue, 03 Aug 2010 15:10:03 +0200 Subject: [Python-checkins] r83543 - python/branches/py3k/Lib/test/regrtest.py In-Reply-To: <20100803015111.DC2E01F451D@kimball.webabinitio.net> References: <20100802185952.CF6BDEEA92@mail.python.org> <20100803015111.DC2E01F451D@kimball.webabinitio.net> Message-ID: Am 03.08.2010 03:51, schrieb R. David Murray: > On Mon, 02 Aug 2010 20:59:52 +0200, georg.brandl wrote: >> Author: georg.brandl >> Date: Mon Aug 2 20:59:52 2010 >> New Revision: 83543 >> >> Log: >> #8560: add progress indicator to regrtest. > [...] >> @@ -517,6 +517,9 @@ >> else: >> tests = iter(selected) >> >> + tests = list(tests) > > I guess you didn't notice that just above this code is a clause that > says 'if forever' which implements -F/--forever by making tests into a > generator that never runs out... Yep, shame one me, but it's fixed now. Georg From python-checkins at python.org Tue Aug 3 18:08:16 2010 From: python-checkins at python.org (mark.dickinson) Date: Tue, 3 Aug 2010 18:08:16 +0200 (CEST) Subject: [Python-checkins] r83667 - in python/branches/py3k: Misc/NEWS Modules/readline.c Message-ID: <20100803160816.AC3FBEE988@mail.python.org> Author: mark.dickinson Date: Tue Aug 3 18:08:16 2010 New Revision: 83667 Log: Issue #9450: Fix memory leaks in readline.remove/replace_history_entry. Modified: python/branches/py3k/Misc/NEWS python/branches/py3k/Modules/readline.c Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Tue Aug 3 18:08:16 2010 @@ -21,6 +21,9 @@ Extensions ---------- +- Issue #9450: Fix memory leak in readline.replace_history_item and + readline.remove_history_item for readline version >= 5.0. + - Issue #8105: Validate file descriptor passed to mmap.mmap on Windows. - Issue #8046: Add context manager protocol support and .closed property Modified: python/branches/py3k/Modules/readline.c ============================================================================== --- python/branches/py3k/Modules/readline.c (original) +++ python/branches/py3k/Modules/readline.c Tue Aug 3 18:08:16 2010 @@ -378,6 +378,38 @@ "set_completer_delims(string) -> None\n\ set the readline word delimiters for tab-completion"); +/* _py_free_history_entry: Utility function to free a history entry. */ + +#if defined(RL_READLINE_VERSION) && RL_READLINE_VERSION >= 0x0500 + +/* Readline version >= 5.0 introduced a timestamp field into the history entry + structure; this needs to be freed to avoid a memory leak. This version of + readline also introduced the handy 'free_history_entry' function, which + takes care of the timestamp. */ + +static void +_py_free_history_entry(HIST_ENTRY *entry) +{ + histdata_t data = free_history_entry(entry); + free(data); +} + +#else + +/* No free_history_entry function; free everything manually. */ + +static void +_py_free_history_entry(HIST_ENTRY *entry) +{ + if (entry->line) + free((void *)entry->line); + if (entry->data) + free(entry->data); + free(entry); +} + +#endif + static PyObject * py_remove_history(PyObject *self, PyObject *args) { @@ -399,12 +431,7 @@ return NULL; } /* free memory allocated for the history entry */ - if (entry->line) - free(entry->line); - if (entry->data) - free(entry->data); - free(entry); - + _py_free_history_entry(entry); Py_RETURN_NONE; } @@ -436,12 +463,7 @@ return NULL; } /* free memory allocated for the old history entry */ - if (old_entry->line) - free(old_entry->line); - if (old_entry->data) - free(old_entry->data); - free(old_entry); - + _py_free_history_entry(old_entry); Py_RETURN_NONE; } From python-checkins at python.org Tue Aug 3 18:14:09 2010 From: python-checkins at python.org (mark.dickinson) Date: Tue, 3 Aug 2010 18:14:09 +0200 (CEST) Subject: [Python-checkins] r83668 - in python/branches/release31-maint: Misc/NEWS Modules/readline.c Message-ID: <20100803161409.4C38CEE988@mail.python.org> Author: mark.dickinson Date: Tue Aug 3 18:14:09 2010 New Revision: 83668 Log: Merged revisions 83667 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83667 | mark.dickinson | 2010-08-03 17:08:16 +0100 (Tue, 03 Aug 2010) | 2 lines Issue #9450: Fix memory leaks in readline.remove/replace_history_entry. ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Misc/NEWS python/branches/release31-maint/Modules/readline.c Modified: python/branches/release31-maint/Misc/NEWS ============================================================================== --- python/branches/release31-maint/Misc/NEWS (original) +++ python/branches/release31-maint/Misc/NEWS Tue Aug 3 18:14:09 2010 @@ -383,6 +383,9 @@ Extension Modules ----------------- +- Issue #9450: Fix memory leak in readline.replace_history_item and + readline.remove_history_item for readline version >= 5.0. + - Issue #8105: Validate file descriptor passed to mmap.mmap on Windows. - Issue #9422: Fix memory leak when re-initializing a struct.Struct object. Modified: python/branches/release31-maint/Modules/readline.c ============================================================================== --- python/branches/release31-maint/Modules/readline.c (original) +++ python/branches/release31-maint/Modules/readline.c Tue Aug 3 18:14:09 2010 @@ -334,6 +334,38 @@ "set_completer_delims(string) -> None\n\ set the readline word delimiters for tab-completion"); +/* _py_free_history_entry: Utility function to free a history entry. */ + +#if defined(RL_READLINE_VERSION) && RL_READLINE_VERSION >= 0x0500 + +/* Readline version >= 5.0 introduced a timestamp field into the history entry + structure; this needs to be freed to avoid a memory leak. This version of + readline also introduced the handy 'free_history_entry' function, which + takes care of the timestamp. */ + +static void +_py_free_history_entry(HIST_ENTRY *entry) +{ + histdata_t data = free_history_entry(entry); + free(data); +} + +#else + +/* No free_history_entry function; free everything manually. */ + +static void +_py_free_history_entry(HIST_ENTRY *entry) +{ + if (entry->line) + free((void *)entry->line); + if (entry->data) + free(entry->data); + free(entry); +} + +#endif + static PyObject * py_remove_history(PyObject *self, PyObject *args) { @@ -355,12 +387,7 @@ return NULL; } /* free memory allocated for the history entry */ - if (entry->line) - free(entry->line); - if (entry->data) - free(entry->data); - free(entry); - + _py_free_history_entry(entry); Py_RETURN_NONE; } @@ -392,12 +419,7 @@ return NULL; } /* free memory allocated for the old history entry */ - if (old_entry->line) - free(old_entry->line); - if (old_entry->data) - free(old_entry->data); - free(old_entry); - + _py_free_history_entry(old_entry); Py_RETURN_NONE; } From python-checkins at python.org Tue Aug 3 18:18:39 2010 From: python-checkins at python.org (mark.dickinson) Date: Tue, 3 Aug 2010 18:18:39 +0200 (CEST) Subject: [Python-checkins] r83669 - in python/branches/release27-maint: Misc/NEWS Modules/readline.c Message-ID: <20100803161839.EABFBEEA0E@mail.python.org> Author: mark.dickinson Date: Tue Aug 3 18:18:39 2010 New Revision: 83669 Log: Merged revisions 83667 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83667 | mark.dickinson | 2010-08-03 17:08:16 +0100 (Tue, 03 Aug 2010) | 2 lines Issue #9450: Fix memory leaks in readline.remove/replace_history_entry. ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Misc/NEWS python/branches/release27-maint/Modules/readline.c Modified: python/branches/release27-maint/Misc/NEWS ============================================================================== --- python/branches/release27-maint/Misc/NEWS (original) +++ python/branches/release27-maint/Misc/NEWS Tue Aug 3 18:18:39 2010 @@ -133,6 +133,9 @@ Extension Modules ----------------- +- Issue #9450: Fix memory leak in readline.replace_history_item and + readline.remove_history_item for readline version >= 5.0. + - Issue #8105: Validate file descriptor passed to mmap.mmap on Windows. - Issue #1019882: Fix IndexError when loading certain hotshot stats. Modified: python/branches/release27-maint/Modules/readline.c ============================================================================== --- python/branches/release27-maint/Modules/readline.c (original) +++ python/branches/release27-maint/Modules/readline.c Tue Aug 3 18:18:39 2010 @@ -356,6 +356,38 @@ "set_completer_delims(string) -> None\n\ set the readline word delimiters for tab-completion"); +/* _py_free_history_entry: Utility function to free a history entry. */ + +#if defined(RL_READLINE_VERSION) && RL_READLINE_VERSION >= 0x0500 + +/* Readline version >= 5.0 introduced a timestamp field into the history entry + structure; this needs to be freed to avoid a memory leak. This version of + readline also introduced the handy 'free_history_entry' function, which + takes care of the timestamp. */ + +static void +_py_free_history_entry(HIST_ENTRY *entry) +{ + histdata_t data = free_history_entry(entry); + free(data); +} + +#else + +/* No free_history_entry function; free everything manually. */ + +static void +_py_free_history_entry(HIST_ENTRY *entry) +{ + if (entry->line) + free((void *)entry->line); + if (entry->data) + free(entry->data); + free(entry); +} + +#endif + static PyObject * py_remove_history(PyObject *self, PyObject *args) { @@ -377,12 +409,7 @@ return NULL; } /* free memory allocated for the history entry */ - if (entry->line) - free((void *)entry->line); - if (entry->data) - free(entry->data); - free(entry); - + _py_free_history_entry(entry); Py_RETURN_NONE; } @@ -414,12 +441,7 @@ return NULL; } /* free memory allocated for the old history entry */ - if (old_entry->line) - free((void *)old_entry->line); - if (old_entry->data) - free(old_entry->data); - free(old_entry); - + _py_free_history_entry(old_entry); Py_RETURN_NONE; } From python-checkins at python.org Tue Aug 3 18:49:49 2010 From: python-checkins at python.org (mark.dickinson) Date: Tue, 3 Aug 2010 18:49:49 +0200 (CEST) Subject: [Python-checkins] r83670 - in python/branches/py3k: Misc/NEWS Modules/readline.c Message-ID: <20100803164949.434BEEE98E@mail.python.org> Author: mark.dickinson Date: Tue Aug 3 18:49:49 2010 New Revision: 83670 Log: Issue #8065: Fix another memory leak in readline module, from failure to free the result of a call to history_get_history_state. Modified: python/branches/py3k/Misc/NEWS python/branches/py3k/Modules/readline.c Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Tue Aug 3 18:49:49 2010 @@ -21,6 +21,9 @@ Extensions ---------- +- Issue #8065: Fix memory leak in readline module (from failure to + free the result of history_get_history_state()). + - Issue #9450: Fix memory leak in readline.replace_history_item and readline.remove_history_item for readline version >= 5.0. Modified: python/branches/py3k/Modules/readline.c ============================================================================== --- python/branches/py3k/Modules/readline.c (original) +++ python/branches/py3k/Modules/readline.c Tue Aug 3 18:49:49 2010 @@ -534,6 +534,25 @@ \n\ Returns current completer function."); +/* Private function to get current length of history. XXX It may be + * possible to replace this with a direct use of history_length instead, + * but it's not clear whether BSD's libedit keeps history_length up to date. + * See issue #8065.*/ + +static int +_py_get_history_length(void) +{ + HISTORY_STATE *hist_st = history_get_history_state(); + int length = hist_st->length; + /* the history docs don't say so, but the address of hist_st changes each + time history_get_history_state is called which makes me think it's + freshly malloc'd memory... on the other hand, the address of the last + line stays the same as long as history isn't extended, so it appears to + be malloc'd but managed by the history package... */ + free(hist_st); + return length; +} + /* Exported function to get any element of history */ static PyObject * @@ -552,9 +571,7 @@ * code doesn't have to worry about the * difference. */ - HISTORY_STATE *hist_st; - hist_st = history_get_history_state(); - + int length = _py_get_history_length(); idx --; /* @@ -562,7 +579,7 @@ * the index is out of range, therefore * test for that and fail gracefully. */ - if (idx < 0 || idx >= hist_st->length) { + if (idx < 0 || idx >= length) { Py_RETURN_NONE; } } @@ -584,10 +601,7 @@ static PyObject * get_current_history_length(PyObject *self, PyObject *noarg) { - HISTORY_STATE *hist_st; - - hist_st = history_get_history_state(); - return PyLong_FromLong(hist_st ? (long) hist_st->length : (long) 0); + return PyLong_FromLong((long)_py_get_history_length()); } PyDoc_STRVAR(doc_get_current_history_length, @@ -1067,29 +1081,22 @@ n = strlen(p); if (n > 0) { char *line; - HISTORY_STATE *state = history_get_history_state(); - if (state->length > 0) + int length = _py_get_history_length(); + if (length > 0) #ifdef __APPLE__ if (using_libedit_emulation) { /* * Libedit's emulation uses 0-based indexes, * the real readline uses 1-based indexes. */ - line = history_get(state->length - 1)->line; + line = history_get(length - 1)->line; } else #endif /* __APPLE__ */ - line = history_get(state->length)->line; + line = history_get(length)->line; else line = ""; if (strcmp(p, line)) add_history(p); - /* the history docs don't say so, but the address of state - changes each time history_get_history_state is called - which makes me think it's freshly malloc'd memory... - on the other hand, the address of the last line stays the - same as long as history isn't extended, so it appears to - be malloc'd but managed by the history package... */ - free(state); } /* Copy the malloc'ed buffer into a PyMem_Malloc'ed one and release the original. */ From python-checkins at python.org Tue Aug 3 18:52:24 2010 From: python-checkins at python.org (mark.dickinson) Date: Tue, 3 Aug 2010 18:52:24 +0200 (CEST) Subject: [Python-checkins] r83671 - in python/branches/release31-maint: Misc/NEWS Modules/readline.c Message-ID: <20100803165224.0C651EE994@mail.python.org> Author: mark.dickinson Date: Tue Aug 3 18:52:23 2010 New Revision: 83671 Log: Merged revisions 83670 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83670 | mark.dickinson | 2010-08-03 17:49:49 +0100 (Tue, 03 Aug 2010) | 3 lines Issue #8065: Fix another memory leak in readline module, from failure to free the result of a call to history_get_history_state. ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Misc/NEWS python/branches/release31-maint/Modules/readline.c Modified: python/branches/release31-maint/Misc/NEWS ============================================================================== --- python/branches/release31-maint/Misc/NEWS (original) +++ python/branches/release31-maint/Misc/NEWS Tue Aug 3 18:52:23 2010 @@ -383,6 +383,9 @@ Extension Modules ----------------- +- Issue #8065: Fix memory leak in readline module (from failure to + free the result of history_get_history_state()). + - Issue #9450: Fix memory leak in readline.replace_history_item and readline.remove_history_item for readline version >= 5.0. Modified: python/branches/release31-maint/Modules/readline.c ============================================================================== --- python/branches/release31-maint/Modules/readline.c (original) +++ python/branches/release31-maint/Modules/readline.c Tue Aug 3 18:52:23 2010 @@ -490,6 +490,25 @@ \n\ Returns current completer function."); +/* Private function to get current length of history. XXX It may be + * possible to replace this with a direct use of history_length instead, + * but it's not clear whether BSD's libedit keeps history_length up to date. + * See issue #8065.*/ + +static int +_py_get_history_length(void) +{ + HISTORY_STATE *hist_st = history_get_history_state(); + int length = hist_st->length; + /* the history docs don't say so, but the address of hist_st changes each + time history_get_history_state is called which makes me think it's + freshly malloc'd memory... on the other hand, the address of the last + line stays the same as long as history isn't extended, so it appears to + be malloc'd but managed by the history package... */ + free(hist_st); + return length; +} + /* Exported function to get any element of history */ static PyObject * @@ -517,10 +536,7 @@ static PyObject * get_current_history_length(PyObject *self, PyObject *noarg) { - HISTORY_STATE *hist_st; - - hist_st = history_get_history_state(); - return PyLong_FromLong(hist_st ? (long) hist_st->length : (long) 0); + return PyLong_FromLong((long)_py_get_history_length()); } PyDoc_STRVAR(doc_get_current_history_length, @@ -1000,20 +1016,13 @@ n = strlen(p); if (n > 0) { char *line; - HISTORY_STATE *state = history_get_history_state(); - if (state->length > 0) - line = history_get(state->length)->line; + int length = _py_get_history_length(); + if (length > 0) + line = history_get(length)->line; else line = ""; if (strcmp(p, line)) add_history(p); - /* the history docs don't say so, but the address of state - changes each time history_get_history_state is called - which makes me think it's freshly malloc'd memory... - on the other hand, the address of the last line stays the - same as long as history isn't extended, so it appears to - be malloc'd but managed by the history package... */ - free(state); } /* Copy the malloc'ed buffer into a PyMem_Malloc'ed one and release the original. */ From python-checkins at python.org Tue Aug 3 18:54:19 2010 From: python-checkins at python.org (mark.dickinson) Date: Tue, 3 Aug 2010 18:54:19 +0200 (CEST) Subject: [Python-checkins] r83672 - in python/branches/release27-maint: Misc/NEWS Modules/readline.c Message-ID: <20100803165419.AFCC3EE994@mail.python.org> Author: mark.dickinson Date: Tue Aug 3 18:54:19 2010 New Revision: 83672 Log: Merged revisions 83670 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83670 | mark.dickinson | 2010-08-03 17:49:49 +0100 (Tue, 03 Aug 2010) | 3 lines Issue #8065: Fix another memory leak in readline module, from failure to free the result of a call to history_get_history_state. ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Misc/NEWS python/branches/release27-maint/Modules/readline.c Modified: python/branches/release27-maint/Misc/NEWS ============================================================================== --- python/branches/release27-maint/Misc/NEWS (original) +++ python/branches/release27-maint/Misc/NEWS Tue Aug 3 18:54:19 2010 @@ -133,6 +133,9 @@ Extension Modules ----------------- +- Issue #8065: Fix memory leak in readline module (from failure to + free the result of history_get_history_state()). + - Issue #9450: Fix memory leak in readline.replace_history_item and readline.remove_history_item for readline version >= 5.0. Modified: python/branches/release27-maint/Modules/readline.c ============================================================================== --- python/branches/release27-maint/Modules/readline.c (original) +++ python/branches/release27-maint/Modules/readline.c Tue Aug 3 18:54:19 2010 @@ -512,6 +512,25 @@ \n\ Returns current completer function."); +/* Private function to get current length of history. XXX It may be + * possible to replace this with a direct use of history_length instead, + * but it's not clear whether BSD's libedit keeps history_length up to date. + * See issue #8065.*/ + +static int +_py_get_history_length(void) +{ + HISTORY_STATE *hist_st = history_get_history_state(); + int length = hist_st->length; + /* the history docs don't say so, but the address of hist_st changes each + time history_get_history_state is called which makes me think it's + freshly malloc'd memory... on the other hand, the address of the last + line stays the same as long as history isn't extended, so it appears to + be malloc'd but managed by the history package... */ + free(hist_st); + return length; +} + /* Exported function to get any element of history */ static PyObject * @@ -530,9 +549,7 @@ * code doesn't have to worry about the * difference. */ - HISTORY_STATE *hist_st; - hist_st = history_get_history_state(); - + int length = _py_get_history_length(); idx --; /* @@ -540,7 +557,7 @@ * the index is out of range, therefore * test for that and fail gracefully. */ - if (idx < 0 || idx >= hist_st->length) { + if (idx < 0 || idx >= length) { Py_RETURN_NONE; } } @@ -562,10 +579,7 @@ static PyObject * get_current_history_length(PyObject *self, PyObject *noarg) { - HISTORY_STATE *hist_st; - - hist_st = history_get_history_state(); - return PyInt_FromLong(hist_st ? (long) hist_st->length : (long) 0); + return PyInt_FromLong((long)_py_get_history_length()); } PyDoc_STRVAR(doc_get_current_history_length, @@ -1046,29 +1060,22 @@ n = strlen(p); if (n > 0) { const char *line; - HISTORY_STATE *state = history_get_history_state(); - if (state->length > 0) + int length = _py_get_history_length(); + if (length > 0) #ifdef __APPLE__ if (using_libedit_emulation) { /* * Libedit's emulation uses 0-based indexes, * the real readline uses 1-based indexes. */ - line = history_get(state->length - 1)->line; + line = history_get(length - 1)->line; } else #endif /* __APPLE__ */ - line = history_get(state->length)->line; + line = history_get(length)->line; else line = ""; if (strcmp(p, line)) add_history(p); - /* the history docs don't say so, but the address of state - changes each time history_get_history_state is called - which makes me think it's freshly malloc'd memory... - on the other hand, the address of the last line stays the - same as long as history isn't extended, so it appears to - be malloc'd but managed by the history package... */ - free(state); } /* Copy the malloc'ed buffer into a PyMem_Malloc'ed one and release the original. */ From python-checkins at python.org Tue Aug 3 19:09:41 2010 From: python-checkins at python.org (antoine.pitrou) Date: Tue, 3 Aug 2010 19:09:41 +0200 (CEST) Subject: [Python-checkins] r83673 - in python/branches/py3k: Misc/NEWS Tools/scripts/serve.py Message-ID: <20100803170941.492C7EEA26@mail.python.org> Author: antoine.pitrou Date: Tue Aug 3 19:09:36 2010 New Revision: 83673 Log: Issue #8867: Fix `Tools/scripts/serve.py` to work with files containing non-ASCII content. Modified: python/branches/py3k/Misc/NEWS python/branches/py3k/Tools/scripts/serve.py Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Tue Aug 3 19:09:36 2010 @@ -85,6 +85,12 @@ - Add lfu_cache() and lru_cache() decorators to the functools module. +Tools/Demos +----------- + +- Issue #8867: Fix ``Tools/scripts/serve.py`` to work with files containing + non-ASCII content. + What's New in Python 3.2 Alpha 1? ================================= Modified: python/branches/py3k/Tools/scripts/serve.py ============================================================================== --- python/branches/py3k/Tools/scripts/serve.py (original) +++ python/branches/py3k/Tools/scripts/serve.py Tue Aug 3 19:09:36 2010 @@ -19,7 +19,7 @@ if os.path.exists(fn): respond('200 OK', [('Content-Type', type)]) - return util.FileWrapper(open(fn)) + return util.FileWrapper(open(fn, "rb")) else: respond('404 Not Found', [('Content-Type', 'text/plain')]) return ['not found'] From python-checkins at python.org Tue Aug 3 19:11:13 2010 From: python-checkins at python.org (antoine.pitrou) Date: Tue, 3 Aug 2010 19:11:13 +0200 (CEST) Subject: [Python-checkins] r83674 - python/branches/release31-maint Message-ID: <20100803171113.80A3BEE9CD@mail.python.org> Author: antoine.pitrou Date: Tue Aug 3 19:11:13 2010 New Revision: 83674 Log: Blocked revisions 83673 via svnmerge ........ r83673 | antoine.pitrou | 2010-08-03 19:09:36 +0200 (mar., 03 ao?t 2010) | 4 lines Issue #8867: Fix `Tools/scripts/serve.py` to work with files containing non-ASCII content. ........ Modified: python/branches/release31-maint/ (props changed) From python-checkins at python.org Tue Aug 3 19:56:09 2010 From: python-checkins at python.org (r.david.murray) Date: Tue, 3 Aug 2010 19:56:09 +0200 (CEST) Subject: [Python-checkins] r83675 - in python/branches/py3k: Doc/library/argparse.rst Lib/argparse.py Lib/test/test_argparse.py Misc/ACKS Misc/NEWS Message-ID: <20100803175609.6E9A7EE987@mail.python.org> Author: r.david.murray Date: Tue Aug 3 19:56:09 2010 New Revision: 83675 Log: #9444: use first of prefix_chars for help opt instead of raising error An argparse option parser created with a prefix_chars that did not include a '-' would happily add -h and --help options, and then throw an error when it tried to format the help because the - was an invalid prefix character. This patch makes it use the first character of prefix_chars as the character for the help options if and only if '-' is not one of the valid prefix_chars. Fix by Theodore Turocy, unit tests by Catherine Devlin. Modified: python/branches/py3k/Doc/library/argparse.rst python/branches/py3k/Lib/argparse.py python/branches/py3k/Lib/test/test_argparse.py python/branches/py3k/Misc/ACKS python/branches/py3k/Misc/NEWS Modified: python/branches/py3k/Doc/library/argparse.rst ============================================================================== --- python/branches/py3k/Doc/library/argparse.rst (original) +++ python/branches/py3k/Doc/library/argparse.rst Tue Aug 3 19:56:09 2010 @@ -203,8 +203,8 @@ add_help ^^^^^^^^ -By default, ArgumentParser objects add a ``-h/--help`` option which simply -displays the parser's help message. For example, consider a file named +By default, ArgumentParser objects add an option which simply displays +the parser's help message. For example, consider a file named ``myprogram.py`` containing the following code:: import argparse @@ -234,12 +234,27 @@ optional arguments: --foo FOO foo help +The help option is typically ``-h/--help``. The exception to this is +if the ``prefix_chars=`` is specified and does not include ``'-'``, in +which case ``-h`` and ``--help`` are not valid options. In +this case, the first character in ``prefix_chars`` is used to prefix +the help options:: + + >>> parser = argparse.ArgumentParser(prog='PROG', prefix_chars='+/') + >>> parser.print_help() + usage: PROG [+h] + + optional arguments: + +h, ++help show this help message and exit + + prefix_chars ^^^^^^^^^^^^ Most command-line options will use ``'-'`` as the prefix, e.g. ``-f/--foo``. -Parsers that need to support additional prefix characters, e.g. for options +Parsers that need to support different or additional prefix +characters, e.g. for options like ``+f`` or ``/foo``, may specify them using the ``prefix_chars=`` argument to the ArgumentParser constructor:: Modified: python/branches/py3k/Lib/argparse.py ============================================================================== --- python/branches/py3k/Lib/argparse.py (original) +++ python/branches/py3k/Lib/argparse.py Tue Aug 3 19:56:09 2010 @@ -1561,13 +1561,16 @@ # add help and version arguments if necessary # (using explicit default to override global argument_default) + default_prefix = '-' if '-' in prefix_chars else prefix_chars[0] if self.add_help: self.add_argument( - '-h', '--help', action='help', default=SUPPRESS, + default_prefix+'h', default_prefix*2+'help', + action='help', default=SUPPRESS, help=_('show this help message and exit')) if self.version: self.add_argument( - '-v', '--version', action='version', default=SUPPRESS, + default_prefix+'v', default_prefix*2+'version', + action='version', default=SUPPRESS, version=self.version, help=_("show program's version number and exit")) Modified: python/branches/py3k/Lib/test/test_argparse.py ============================================================================== --- python/branches/py3k/Lib/test/test_argparse.py (original) +++ python/branches/py3k/Lib/test/test_argparse.py Tue Aug 3 19:56:09 2010 @@ -417,7 +417,7 @@ class TestOptionalsAlternatePrefixChars(ParserTestCase): - """Test an Optional with a double-dash option string""" + """Test an Optional with option strings with custom prefixes""" parser_signature = Sig(prefix_chars='+:/', add_help=False) argument_signatures = [ @@ -425,7 +425,7 @@ Sig('::bar'), Sig('/baz', action='store_const', const=42), ] - failures = ['--bar', '-fbar', '-b B', 'B', '-f', '--bar B', '-baz'] + failures = ['--bar', '-fbar', '-b B', 'B', '-f', '--bar B', '-baz', '-h', '--help', '+h', '::help', '/help'] successes = [ ('', NS(f=False, bar=None, baz=None)), ('+f', NS(f=True, bar=None, baz=None)), @@ -436,6 +436,27 @@ ] +class TestOptionalsAlternatePrefixCharsAddedHelp(ParserTestCase): + """When ``-`` not in prefix_chars, default operators created for help + should use the prefix_chars in use rather than - or -- + http://bugs.python.org/issue9444""" + + parser_signature = Sig(prefix_chars='+:/', add_help=True) + argument_signatures = [ + Sig('+f', action='store_true'), + Sig('::bar'), + Sig('/baz', action='store_const', const=42), + ] + failures = ['--bar', '-fbar', '-b B', 'B', '-f', '--bar B', '-baz'] + successes = [ + ('', NS(f=False, bar=None, baz=None)), + ('+f', NS(f=True, bar=None, baz=None)), + ('::ba B', NS(f=False, bar='B', baz=None)), + ('+f ::bar B', NS(f=True, bar='B', baz=None)), + ('+f /b', NS(f=True, bar=None, baz=42)), + ('/ba +f', NS(f=True, bar=None, baz=42)) + ] + class TestOptionalsShortLong(ParserTestCase): """Test a combination of single- and double-dash option strings""" @@ -1655,12 +1676,18 @@ def assertArgumentParserError(self, *args, **kwargs): self.assertRaises(ArgumentParserError, *args, **kwargs) - def _get_parser(self, subparser_help=False): + def _get_parser(self, subparser_help=False, prefix_chars=None): # create a parser with a subparsers argument - parser = ErrorRaisingArgumentParser( - prog='PROG', description='main description') - parser.add_argument( - '--foo', action='store_true', help='foo help') + if prefix_chars: + parser = ErrorRaisingArgumentParser( + prog='PROG', description='main description', prefix_chars=prefix_chars) + parser.add_argument( + prefix_chars[0] * 2 + 'foo', action='store_true', help='foo help') + else: + parser = ErrorRaisingArgumentParser( + prog='PROG', description='main description') + parser.add_argument( + '--foo', action='store_true', help='foo help') parser.add_argument( 'bar', type=float, help='bar help') @@ -1739,6 +1766,44 @@ --foo foo help ''')) + def test_help_extra_prefix_chars(self): + # Make sure - is still used for help if it is a non-first prefix char + parser = self._get_parser(prefix_chars='+:-') + self.assertEqual(parser.format_usage(), + 'usage: PROG [-h] [++foo] bar {1,2} ...\n') + self.assertEqual(parser.format_help(), textwrap.dedent('''\ + usage: PROG [-h] [++foo] bar {1,2} ... + + main description + + positional arguments: + bar bar help + {1,2} command help + + optional arguments: + -h, --help show this help message and exit + ++foo foo help + ''')) + + + def test_help_alternate_prefix_chars(self): + parser = self._get_parser(prefix_chars='+:/') + self.assertEqual(parser.format_usage(), + 'usage: PROG [+h] [++foo] bar {1,2} ...\n') + self.assertEqual(parser.format_help(), textwrap.dedent('''\ + usage: PROG [+h] [++foo] bar {1,2} ... + + main description + + positional arguments: + bar bar help + {1,2} command help + + optional arguments: + +h, ++help show this help message and exit + ++foo foo help + ''')) + def test_parser_command_help(self): self.assertEqual(self.command_help_parser.format_usage(), 'usage: PROG [-h] [--foo] bar {1,2} ...\n') Modified: python/branches/py3k/Misc/ACKS ============================================================================== --- python/branches/py3k/Misc/ACKS (original) +++ python/branches/py3k/Misc/ACKS Tue Aug 3 19:56:09 2010 @@ -802,6 +802,7 @@ Jason Trowbridge Anthony Tuininga Stephen Turner +Theodore Turocy Bill Tutt Doobee R. Tzeck Eren T?rkay Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Tue Aug 3 19:56:09 2010 @@ -35,6 +35,10 @@ Library ------- +- Issue #9444: Argparse now uses the first element of prefix_chars as + the option character for the added 'h/help' option if prefix_chars + does not contain a '-', instead of raising an error. + - Issue #7372: Fix pstats regression when stripping paths from profile data generated with the profile module. From python-checkins at python.org Tue Aug 3 20:14:01 2010 From: python-checkins at python.org (r.david.murray) Date: Tue, 3 Aug 2010 20:14:01 +0200 (CEST) Subject: [Python-checkins] r83676 - in python/branches/release27-maint: Doc/library/argparse.rst Lib/argparse.py Lib/test/test_argparse.py Misc/ACKS Misc/NEWS Message-ID: <20100803181401.BFE3EEE98A@mail.python.org> Author: r.david.murray Date: Tue Aug 3 20:14:01 2010 New Revision: 83676 Log: Merged revisions 83675 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83675 | r.david.murray | 2010-08-03 13:56:09 -0400 (Tue, 03 Aug 2010) | 12 lines #9444: use first of prefix_chars for help opt instead of raising error An argparse option parser created with a prefix_chars that did not include a '-' would happily add -h and --help options, and then throw an error when it tried to format the help because the - was an invalid prefix character. This patch makes it use the first character of prefix_chars as the character for the help options if and only if '-' is not one of the valid prefix_chars. Fix by Theodore Turocy, unit tests by Catherine Devlin. ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Doc/library/argparse.rst python/branches/release27-maint/Lib/argparse.py python/branches/release27-maint/Lib/test/test_argparse.py python/branches/release27-maint/Misc/ACKS python/branches/release27-maint/Misc/NEWS Modified: python/branches/release27-maint/Doc/library/argparse.rst ============================================================================== --- python/branches/release27-maint/Doc/library/argparse.rst (original) +++ python/branches/release27-maint/Doc/library/argparse.rst Tue Aug 3 20:14:01 2010 @@ -203,8 +203,8 @@ add_help ^^^^^^^^ -By default, ArgumentParser objects add a ``-h/--help`` option which simply -displays the parser's help message. For example, consider a file named +By default, ArgumentParser objects add an option which simply displays +the parser's help message. For example, consider a file named ``myprogram.py`` containing the following code:: import argparse @@ -234,12 +234,27 @@ optional arguments: --foo FOO foo help +The help option is typically ``-h/--help``. The exception to this is +if the ``prefix_chars=`` is specified and does not include ``'-'``, in +which case ``-h`` and ``--help`` are not valid options. In +this case, the first character in ``prefix_chars`` is used to prefix +the help options:: + + >>> parser = argparse.ArgumentParser(prog='PROG', prefix_chars='+/') + >>> parser.print_help() + usage: PROG [+h] + + optional arguments: + +h, ++help show this help message and exit + + prefix_chars ^^^^^^^^^^^^ Most command-line options will use ``'-'`` as the prefix, e.g. ``-f/--foo``. -Parsers that need to support additional prefix characters, e.g. for options +Parsers that need to support different or additional prefix +characters, e.g. for options like ``+f`` or ``/foo``, may specify them using the ``prefix_chars=`` argument to the ArgumentParser constructor:: Modified: python/branches/release27-maint/Lib/argparse.py ============================================================================== --- python/branches/release27-maint/Lib/argparse.py (original) +++ python/branches/release27-maint/Lib/argparse.py Tue Aug 3 20:14:01 2010 @@ -1563,13 +1563,16 @@ # add help and version arguments if necessary # (using explicit default to override global argument_default) + default_prefix = '-' if '-' in prefix_chars else prefix_chars[0] if self.add_help: self.add_argument( - '-h', '--help', action='help', default=SUPPRESS, + default_prefix+'h', default_prefix*2+'help', + action='help', default=SUPPRESS, help=_('show this help message and exit')) if self.version: self.add_argument( - '-v', '--version', action='version', default=SUPPRESS, + default_prefix+'v', default_prefix*2+'version', + action='version', default=SUPPRESS, version=self.version, help=_("show program's version number and exit")) Modified: python/branches/release27-maint/Lib/test/test_argparse.py ============================================================================== --- python/branches/release27-maint/Lib/test/test_argparse.py (original) +++ python/branches/release27-maint/Lib/test/test_argparse.py Tue Aug 3 20:14:01 2010 @@ -420,7 +420,7 @@ class TestOptionalsAlternatePrefixChars(ParserTestCase): - """Test an Optional with a double-dash option string""" + """Test an Optional with option strings with custom prefixes""" parser_signature = Sig(prefix_chars='+:/', add_help=False) argument_signatures = [ @@ -428,7 +428,7 @@ Sig('::bar'), Sig('/baz', action='store_const', const=42), ] - failures = ['--bar', '-fbar', '-b B', 'B', '-f', '--bar B', '-baz'] + failures = ['--bar', '-fbar', '-b B', 'B', '-f', '--bar B', '-baz', '-h', '--help', '+h', '::help', '/help'] successes = [ ('', NS(f=False, bar=None, baz=None)), ('+f', NS(f=True, bar=None, baz=None)), @@ -439,6 +439,27 @@ ] +class TestOptionalsAlternatePrefixCharsAddedHelp(ParserTestCase): + """When ``-`` not in prefix_chars, default operators created for help + should use the prefix_chars in use rather than - or -- + http://bugs.python.org/issue9444""" + + parser_signature = Sig(prefix_chars='+:/', add_help=True) + argument_signatures = [ + Sig('+f', action='store_true'), + Sig('::bar'), + Sig('/baz', action='store_const', const=42), + ] + failures = ['--bar', '-fbar', '-b B', 'B', '-f', '--bar B', '-baz'] + successes = [ + ('', NS(f=False, bar=None, baz=None)), + ('+f', NS(f=True, bar=None, baz=None)), + ('::ba B', NS(f=False, bar='B', baz=None)), + ('+f ::bar B', NS(f=True, bar='B', baz=None)), + ('+f /b', NS(f=True, bar=None, baz=42)), + ('/ba +f', NS(f=True, bar=None, baz=42)) + ] + class TestOptionalsShortLong(ParserTestCase): """Test a combination of single- and double-dash option strings""" @@ -1666,12 +1687,18 @@ def assertArgumentParserError(self, *args, **kwargs): self.assertRaises(ArgumentParserError, *args, **kwargs) - def _get_parser(self, subparser_help=False): + def _get_parser(self, subparser_help=False, prefix_chars=None): # create a parser with a subparsers argument - parser = ErrorRaisingArgumentParser( - prog='PROG', description='main description') - parser.add_argument( - '--foo', action='store_true', help='foo help') + if prefix_chars: + parser = ErrorRaisingArgumentParser( + prog='PROG', description='main description', prefix_chars=prefix_chars) + parser.add_argument( + prefix_chars[0] * 2 + 'foo', action='store_true', help='foo help') + else: + parser = ErrorRaisingArgumentParser( + prog='PROG', description='main description') + parser.add_argument( + '--foo', action='store_true', help='foo help') parser.add_argument( 'bar', type=float, help='bar help') @@ -1750,6 +1777,44 @@ --foo foo help ''')) + def test_help_extra_prefix_chars(self): + # Make sure - is still used for help if it is a non-first prefix char + parser = self._get_parser(prefix_chars='+:-') + self.assertEqual(parser.format_usage(), + 'usage: PROG [-h] [++foo] bar {1,2} ...\n') + self.assertEqual(parser.format_help(), textwrap.dedent('''\ + usage: PROG [-h] [++foo] bar {1,2} ... + + main description + + positional arguments: + bar bar help + {1,2} command help + + optional arguments: + -h, --help show this help message and exit + ++foo foo help + ''')) + + + def test_help_alternate_prefix_chars(self): + parser = self._get_parser(prefix_chars='+:/') + self.assertEqual(parser.format_usage(), + 'usage: PROG [+h] [++foo] bar {1,2} ...\n') + self.assertEqual(parser.format_help(), textwrap.dedent('''\ + usage: PROG [+h] [++foo] bar {1,2} ... + + main description + + positional arguments: + bar bar help + {1,2} command help + + optional arguments: + +h, ++help show this help message and exit + ++foo foo help + ''')) + def test_parser_command_help(self): self.assertEqual(self.command_help_parser.format_usage(), 'usage: PROG [-h] [--foo] bar {1,2} ...\n') Modified: python/branches/release27-maint/Misc/ACKS ============================================================================== --- python/branches/release27-maint/Misc/ACKS (original) +++ python/branches/release27-maint/Misc/ACKS Tue Aug 3 20:14:01 2010 @@ -782,6 +782,7 @@ Jason Trowbridge Anthony Tuininga Stephen Turner +Theodore Turocy Bill Tutt Doobee R. Tzeck Eren T?rkay Modified: python/branches/release27-maint/Misc/NEWS ============================================================================== --- python/branches/release27-maint/Misc/NEWS (original) +++ python/branches/release27-maint/Misc/NEWS Tue Aug 3 20:14:01 2010 @@ -24,6 +24,10 @@ Library ------- +- Issue #9444: Argparse now uses the first element of prefix_chars as + the option character for the added 'h/help' option if prefix_chars + does not contain a '-', instead of raising an error. + - Issue #9354: Provide getsockopt() in asyncore's file_wrapper. - Issue #7781: Fix restricting stats by entry counts in the pstats From python-checkins at python.org Tue Aug 3 20:31:54 2010 From: python-checkins at python.org (mark.dickinson) Date: Tue, 3 Aug 2010 20:31:54 +0200 (CEST) Subject: [Python-checkins] r83677 - python/branches/py3k/Modules/_ssl.c Message-ID: <20100803183154.63678EE98E@mail.python.org> Author: mark.dickinson Date: Tue Aug 3 20:31:54 2010 New Revision: 83677 Log: Fix memory leak in ssl module. Modified: python/branches/py3k/Modules/_ssl.c Modified: python/branches/py3k/Modules/_ssl.c ============================================================================== --- python/branches/py3k/Modules/_ssl.c (original) +++ python/branches/py3k/Modules/_ssl.c Tue Aug 3 20:31:54 2010 @@ -857,6 +857,7 @@ } retval = _decode_certificate(x, verbose); + X509_free(x); fail0: Py_DECREF(filename); From python-checkins at python.org Tue Aug 3 20:32:26 2010 From: python-checkins at python.org (antoine.pitrou) Date: Tue, 3 Aug 2010 20:32:26 +0200 (CEST) Subject: [Python-checkins] r83678 - python/branches/py3k/Lib/test/test_threading_local.py Message-ID: <20100803183226.48C99EE98E@mail.python.org> Author: antoine.pitrou Date: Tue Aug 3 20:32:26 2010 New Revision: 83678 Log: In test_threading_local, test both the default _thread._local implementation and the pure Python implementation in Lib/_threading_local.py Modified: python/branches/py3k/Lib/test/test_threading_local.py Modified: python/branches/py3k/Lib/test/test_threading_local.py ============================================================================== --- python/branches/py3k/Lib/test/test_threading_local.py (original) +++ python/branches/py3k/Lib/test/test_threading_local.py Tue Aug 3 20:32:26 2010 @@ -1,10 +1,15 @@ import unittest from doctest import DocTestSuite from test import support -threading = support.import_module('threading') import weakref import gc +# Modules under test +_thread = support.import_module('_thread') +threading = support.import_module('threading') +import _threading_local + + class Weak(object): pass @@ -13,7 +18,8 @@ local.weak = weak weaklist.append(weakref.ref(weak)) -class ThreadingLocalTest(unittest.TestCase): + +class BaseLocalTest: def test_local_refs(self): self._local_refs(20) @@ -21,7 +27,7 @@ self._local_refs(100) def _local_refs(self, n): - local = threading.local() + local = self._local() weaklist = [] for i in range(n): t = threading.Thread(target=target, args=(local, weaklist)) @@ -48,7 +54,7 @@ # is created but not correctly set on the object. # The first member set may be bogus. import time - class Local(threading.local): + class Local(self._local): def __init__(self): time.sleep(0.01) local = Local() @@ -69,7 +75,7 @@ def test_derived_cycle_dealloc(self): # http://bugs.python.org/issue6990 - class Local(threading.local): + class Local(self._local): pass locals = None passed = False @@ -108,24 +114,28 @@ def test_arguments(self): # Issue 1522237 - from _thread import _local as local - from _threading_local import local as py_local + class MyLocal(self._local): + def __init__(self, *args, **kwargs): + pass + + MyLocal(a=1) + MyLocal(1) + self.assertRaises(TypeError, self._local, a=1) + self.assertRaises(TypeError, self._local, 1) + + +class ThreadLocalTest(unittest.TestCase, BaseLocalTest): + _local = _thread._local - for cls in (local, py_local): - class MyLocal(cls): - def __init__(self, *args, **kwargs): - pass - - MyLocal(a=1) - MyLocal(1) - self.assertRaises(TypeError, cls, a=1) - self.assertRaises(TypeError, cls, 1) +class PyThreadingLocalTest(unittest.TestCase, BaseLocalTest): + _local = _threading_local.local def test_main(): suite = unittest.TestSuite() suite.addTest(DocTestSuite('_threading_local')) - suite.addTest(unittest.makeSuite(ThreadingLocalTest)) + suite.addTest(unittest.makeSuite(ThreadLocalTest)) + suite.addTest(unittest.makeSuite(PyThreadingLocalTest)) try: from thread import _local From python-checkins at python.org Tue Aug 3 20:33:11 2010 From: python-checkins at python.org (mark.dickinson) Date: Tue, 3 Aug 2010 20:33:11 +0200 (CEST) Subject: [Python-checkins] r83679 - in python/branches/release31-maint: Modules/_ssl.c Message-ID: <20100803183311.E1AA4EE98E@mail.python.org> Author: mark.dickinson Date: Tue Aug 3 20:33:11 2010 New Revision: 83679 Log: Merged revisions 83677 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83677 | mark.dickinson | 2010-08-03 19:31:54 +0100 (Tue, 03 Aug 2010) | 1 line Fix memory leak in ssl module. ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Modules/_ssl.c Modified: python/branches/release31-maint/Modules/_ssl.c ============================================================================== --- python/branches/release31-maint/Modules/_ssl.c (original) +++ python/branches/release31-maint/Modules/_ssl.c Tue Aug 3 20:33:11 2010 @@ -977,6 +977,7 @@ } retval = _decode_certificate(x, verbose); + X509_free(x); fail0: From python-checkins at python.org Tue Aug 3 20:34:53 2010 From: python-checkins at python.org (mark.dickinson) Date: Tue, 3 Aug 2010 20:34:53 +0200 (CEST) Subject: [Python-checkins] r83680 - in python/branches/release27-maint: Modules/_ssl.c Message-ID: <20100803183453.ADEB5EE994@mail.python.org> Author: mark.dickinson Date: Tue Aug 3 20:34:53 2010 New Revision: 83680 Log: Merged revisions 83677 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83677 | mark.dickinson | 2010-08-03 19:31:54 +0100 (Tue, 03 Aug 2010) | 1 line Fix memory leak in ssl module. ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Modules/_ssl.c Modified: python/branches/release27-maint/Modules/_ssl.c ============================================================================== --- python/branches/release27-maint/Modules/_ssl.c (original) +++ python/branches/release27-maint/Modules/_ssl.c Tue Aug 3 20:34:53 2010 @@ -978,6 +978,7 @@ } retval = _decode_certificate(x, verbose); + X509_free(x); fail0: From python-checkins at python.org Tue Aug 3 20:35:55 2010 From: python-checkins at python.org (martin.v.loewis) Date: Tue, 3 Aug 2010 20:35:55 +0200 (CEST) Subject: [Python-checkins] r83681 - in python/branches/py3k/Tools/msi: msi.py msilib.py Message-ID: <20100803183555.4FA48EEA30@mail.python.org> Author: martin.v.loewis Date: Tue Aug 3 20:35:55 2010 New Revision: 83681 Log: Add various missing files. Improve detection of unpackaged files. Modified: python/branches/py3k/Tools/msi/msi.py python/branches/py3k/Tools/msi/msilib.py Modified: python/branches/py3k/Tools/msi/msi.py ============================================================================== --- python/branches/py3k/Tools/msi/msi.py (original) +++ python/branches/py3k/Tools/msi/msi.py Tue Aug 3 20:35:55 2010 @@ -902,6 +902,13 @@ kw['componentflags'] = 2 #msidbComponentAttributesOptional Directory.__init__(self, *args, **kw) + def check_unpackaged(self): + self.unpackaged_files.discard('__pycache__') + self.unpackaged_files.discard('.svn') + if self.unpackaged_files: + print "Warning: Unpackaged files in %s" % self.absolute + print self.unpackaged_files + # See "File Table", "Component Table", "Directory Table", # "FeatureComponents Table" def add_files(db): @@ -965,13 +972,13 @@ extensions.remove("_ctypes.pyd") # Add all .py files in Lib, except tkinter, test - dirs={} + dirs = [] pydirs = [(root,"Lib")] while pydirs: # Commit every now and then, or else installer will complain db.Commit() parent, dir = pydirs.pop() - if dir == ".svn" or dir.startswith("plat-"): + if dir == ".svn" or dir == '__pycache__' or dir.startswith("plat-"): continue elif dir in ["tkinter", "idlelib", "Icons"]: if not have_tcl: @@ -989,7 +996,7 @@ default_feature.set_current() lib = PyDirectory(db, cab, parent, dir, dir, "%s|%s" % (parent.make_short(dir), dir)) # Add additional files - dirs[dir]=lib + dirs.append(lib) lib.glob("*.txt") if dir=='site-packages': lib.add_file("README.txt", src="README") @@ -999,18 +1006,13 @@ if files: # Add an entry to the RemoveFile table to remove bytecode files. lib.remove_pyc() - if dir.endswith('.egg-info'): - lib.add_file('entry_points.txt') - lib.add_file('PKG-INFO') - lib.add_file('top_level.txt') - lib.add_file('zip-safe') - continue + # package READMEs if present + lib.glob("README") + if dir=='Lib': + lib.add_file('wsgiref.egg-info') if dir=='test' and parent.physical=='Lib': lib.add_file("185test.db") lib.add_file("audiotest.au") - lib.add_file("cfgparser.1") - lib.add_file("cfgparser.2") - lib.add_file("cfgparser.3") lib.add_file("sgml_input.html") lib.add_file("testtar.tar") lib.add_file("test_difflib_expect.html") @@ -1020,7 +1022,10 @@ lib.glob("*.uue") lib.glob("*.pem") lib.glob("*.pck") + lib.glob("cfgparser.*") lib.add_file("zipdir.zip") + if dir=='capath': + lib.glob("*.0") if dir=='tests' and parent.physical=='distutils': lib.add_file("Setup.sample") if dir=='decimaltestdata': @@ -1030,19 +1035,26 @@ lib.add_file("test.xml.out") if dir=='output': lib.glob("test_*") + if dir=='sndhdrdata': + lib.glob("sndhdr.*") if dir=='idlelib': lib.glob("*.def") lib.add_file("idle.bat") + lib.add_file("ChangeLog") if dir=="Icons": lib.glob("*.gif") lib.add_file("idle.icns") if dir=="command" and parent.physical=="distutils": lib.glob("wininst*.exe") + lib.add_file("command_template") if dir=="setuptools": lib.add_file("cli.exe") lib.add_file("gui.exe") if dir=="lib2to3": lib.removefile("pickle", "*.pickle") + if dir=="macholib": + lib.add_file("README.ctypes") + lib.glob("fetch_macholib*") if dir=="data" and parent.physical=="test" and parent.basedir.physical=="email": # This should contain all non-.svn files listed in subversion for f in os.listdir(lib.absolute): @@ -1054,6 +1066,8 @@ for f in os.listdir(lib.absolute): if os.path.isdir(os.path.join(lib.absolute, f)): pydirs.append((lib, f)) + for d in dirs: + d.check_unpackaged() # Add DLLs default_feature.set_current() lib = DLLs Modified: python/branches/py3k/Tools/msi/msilib.py ============================================================================== --- python/branches/py3k/Tools/msi/msilib.py (original) +++ python/branches/py3k/Tools/msi/msilib.py Tue Aug 3 20:35:55 2010 @@ -451,6 +451,12 @@ else: self.absolute = physical blogical = None + # initially assume that all files in this directory are unpackaged + # as files from self.absolute get added, this set is reduced + self.unpackaged_files = set() + for f in os.listdir(self.absolute): + if os.path.isfile(os.path.join(self.absolute, f)): + self.unpackaged_files.add(f) add_data(db, "Directory", [(logical, blogical, default)]) def start_component(self, component = None, feature = None, flags = None, keyfile = None, uuid=None): @@ -527,6 +533,11 @@ src = file file = os.path.basename(file) absolute = os.path.join(self.absolute, src) + if absolute.startswith(self.absolute): + # mark file as packaged + relative = absolute[len(self.absolute)+1:] + if relative in self.unpackaged_files: + self.unpackaged_files.remove(relative) assert not re.search(r'[\?|><:/*]"', file) # restrictions on long names if self.keyfiles.has_key(file): logical = self.keyfiles[file] From python-checkins at python.org Tue Aug 3 20:41:05 2010 From: python-checkins at python.org (antoine.pitrou) Date: Tue, 3 Aug 2010 20:41:05 +0200 (CEST) Subject: [Python-checkins] r83682 - in python/branches/release31-maint: Lib/test/test_threading_local.py Message-ID: <20100803184105.849F8EE9A4@mail.python.org> Author: antoine.pitrou Date: Tue Aug 3 20:41:05 2010 New Revision: 83682 Log: Recorded merge of revisions 83678 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83678 | antoine.pitrou | 2010-08-03 20:32:26 +0200 (mar., 03 ao?t 2010) | 4 lines In test_threading_local, test both the default _thread._local implementation and the pure Python implementation in Lib/_threading_local.py ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Lib/test/test_threading_local.py Modified: python/branches/release31-maint/Lib/test/test_threading_local.py ============================================================================== --- python/branches/release31-maint/Lib/test/test_threading_local.py (original) +++ python/branches/release31-maint/Lib/test/test_threading_local.py Tue Aug 3 20:41:05 2010 @@ -1,10 +1,15 @@ import unittest from doctest import DocTestSuite from test import support -import threading import weakref import gc +# Modules under test +_thread = support.import_module('_thread') +threading = support.import_module('threading') +import _threading_local + + class Weak(object): pass @@ -13,7 +18,8 @@ local.weak = weak weaklist.append(weakref.ref(weak)) -class ThreadingLocalTest(unittest.TestCase): + +class BaseLocalTest: def test_local_refs(self): self._local_refs(20) @@ -21,7 +27,7 @@ self._local_refs(100) def _local_refs(self, n): - local = threading.local() + local = self._local() weaklist = [] for i in range(n): t = threading.Thread(target=target, args=(local, weaklist)) @@ -48,7 +54,7 @@ # is created but not correctly set on the object. # The first member set may be bogus. import time - class Local(threading.local): + class Local(self._local): def __init__(self): time.sleep(0.01) local = Local() @@ -69,7 +75,7 @@ def test_derived_cycle_dealloc(self): # http://bugs.python.org/issue6990 - class Local(threading.local): + class Local(self._local): pass locals = None passed = False @@ -107,10 +113,18 @@ self.assertTrue(passed) +class ThreadLocalTest(unittest.TestCase, BaseLocalTest): + _local = _thread._local + +class PyThreadingLocalTest(unittest.TestCase, BaseLocalTest): + _local = _threading_local.local + + def test_main(): suite = unittest.TestSuite() suite.addTest(DocTestSuite('_threading_local')) - suite.addTest(unittest.makeSuite(ThreadingLocalTest)) + suite.addTest(unittest.makeSuite(ThreadLocalTest)) + suite.addTest(unittest.makeSuite(PyThreadingLocalTest)) try: from thread import _local From python-checkins at python.org Tue Aug 3 20:44:16 2010 From: python-checkins at python.org (mark.dickinson) Date: Tue, 3 Aug 2010 20:44:16 +0200 (CEST) Subject: [Python-checkins] r83683 - python/branches/py3k/Misc/NEWS Message-ID: <20100803184416.912A3EEA43@mail.python.org> Author: mark.dickinson Date: Tue Aug 3 20:44:16 2010 New Revision: 83683 Log: Misc/NEWS entry for r83677. Modified: python/branches/py3k/Misc/NEWS Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Tue Aug 3 20:44:16 2010 @@ -21,6 +21,8 @@ Extensions ---------- +- Fix memory leak in ssl._ssl._test_decode_cert. + - Issue #8065: Fix memory leak in readline module (from failure to free the result of history_get_history_state()). From python-checkins at python.org Tue Aug 3 20:45:30 2010 From: python-checkins at python.org (mark.dickinson) Date: Tue, 3 Aug 2010 20:45:30 +0200 (CEST) Subject: [Python-checkins] r83684 - in python/branches/release31-maint: Misc/NEWS Message-ID: <20100803184530.C6179EBD3@mail.python.org> Author: mark.dickinson Date: Tue Aug 3 20:45:30 2010 New Revision: 83684 Log: Merged revisions 83683 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83683 | mark.dickinson | 2010-08-03 19:44:16 +0100 (Tue, 03 Aug 2010) | 1 line Misc/NEWS entry for r83677. ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Misc/NEWS Modified: python/branches/release31-maint/Misc/NEWS ============================================================================== --- python/branches/release31-maint/Misc/NEWS (original) +++ python/branches/release31-maint/Misc/NEWS Tue Aug 3 20:45:30 2010 @@ -383,6 +383,8 @@ Extension Modules ----------------- +- Fix memory leak in ssl._ssl._test_decode_cert. + - Issue #8065: Fix memory leak in readline module (from failure to free the result of history_get_history_state()). From python-checkins at python.org Tue Aug 3 20:46:28 2010 From: python-checkins at python.org (mark.dickinson) Date: Tue, 3 Aug 2010 20:46:28 +0200 (CEST) Subject: [Python-checkins] r83685 - in python/branches/release27-maint: Misc/NEWS Message-ID: <20100803184628.58137EEA1B@mail.python.org> Author: mark.dickinson Date: Tue Aug 3 20:46:28 2010 New Revision: 83685 Log: Merged revisions 83683 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83683 | mark.dickinson | 2010-08-03 19:44:16 +0100 (Tue, 03 Aug 2010) | 1 line Misc/NEWS entry for r83677. ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Misc/NEWS Modified: python/branches/release27-maint/Misc/NEWS ============================================================================== --- python/branches/release27-maint/Misc/NEWS (original) +++ python/branches/release27-maint/Misc/NEWS Tue Aug 3 20:46:28 2010 @@ -137,6 +137,8 @@ Extension Modules ----------------- +- Fix memory leak in ssl._ssl._test_decode_cert. + - Issue #8065: Fix memory leak in readline module (from failure to free the result of history_get_history_state()). From python-checkins at python.org Tue Aug 3 20:50:32 2010 From: python-checkins at python.org (mark.dickinson) Date: Tue, 3 Aug 2010 20:50:32 +0200 (CEST) Subject: [Python-checkins] r83686 - in python/branches/release26-maint: Misc/NEWS Modules/_ssl.c Message-ID: <20100803185032.DAE18EEA1B@mail.python.org> Author: mark.dickinson Date: Tue Aug 3 20:50:32 2010 New Revision: 83686 Log: Merged revisions 83680,83685 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/release27-maint ................ r83680 | mark.dickinson | 2010-08-03 19:34:53 +0100 (Tue, 03 Aug 2010) | 9 lines Merged revisions 83677 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83677 | mark.dickinson | 2010-08-03 19:31:54 +0100 (Tue, 03 Aug 2010) | 1 line Fix memory leak in ssl module. ........ ................ r83685 | mark.dickinson | 2010-08-03 19:46:28 +0100 (Tue, 03 Aug 2010) | 9 lines Merged revisions 83683 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83683 | mark.dickinson | 2010-08-03 19:44:16 +0100 (Tue, 03 Aug 2010) | 1 line Misc/NEWS entry for r83677. ........ ................ Modified: python/branches/release26-maint/ (props changed) python/branches/release26-maint/Misc/NEWS python/branches/release26-maint/Modules/_ssl.c Modified: python/branches/release26-maint/Misc/NEWS ============================================================================== --- python/branches/release26-maint/Misc/NEWS (original) +++ python/branches/release26-maint/Misc/NEWS Tue Aug 3 20:50:32 2010 @@ -379,6 +379,8 @@ Extension Modules ----------------- +- Fix memory leak in ssl._ssl._test_decode_cert. + - Issue #9422: Fix memory leak when re-initializing a struct.Struct object. - Issue #7900: The getgroups(2) system call on MacOSX behaves rather oddly Modified: python/branches/release26-maint/Modules/_ssl.c ============================================================================== --- python/branches/release26-maint/Modules/_ssl.c (original) +++ python/branches/release26-maint/Modules/_ssl.c Tue Aug 3 20:50:32 2010 @@ -964,6 +964,7 @@ } retval = _decode_certificate(x, verbose); + X509_free(x); fail0: From python-checkins at python.org Tue Aug 3 21:43:08 2010 From: python-checkins at python.org (ezio.melotti) Date: Tue, 3 Aug 2010 21:43:08 +0200 (CEST) Subject: [Python-checkins] r83687 - in python/branches/release26-maint/Lib/test: regrtest.py test_dl.py Message-ID: <20100803194308.D0128EEA24@mail.python.org> Author: ezio.melotti Date: Tue Aug 3 21:43:08 2010 New Revision: 83687 Log: Run test_py3kwarn first to avoid failures with -3. Modified: python/branches/release26-maint/Lib/test/regrtest.py python/branches/release26-maint/Lib/test/test_dl.py Modified: python/branches/release26-maint/Lib/test/regrtest.py ============================================================================== --- python/branches/release26-maint/Lib/test/regrtest.py (original) +++ python/branches/release26-maint/Lib/test/regrtest.py Tue Aug 3 21:43:08 2010 @@ -483,6 +483,13 @@ 'test_unittest', 'test_doctest', 'test_doctest2', + # On 2.6, when a C module like dl or linuxaudiodev is imported by some + # test, a DeprecationWarning is raised, but test_py3kwarn can not find + # it in the __warningregistry__ of the modules in sys.modules. + # C modules raise the warning only once, and since there's no way to + # find these warnings, test_py3kwarn is executed first to catch them + # before the other modules. This shouldn't affect 2.7+ + 'test_py3kwarn', ] NOTTESTS = [ Modified: python/branches/release26-maint/Lib/test/test_dl.py ============================================================================== --- python/branches/release26-maint/Lib/test/test_dl.py (original) +++ python/branches/release26-maint/Lib/test/test_dl.py Tue Aug 3 21:43:08 2010 @@ -3,10 +3,7 @@ Roger E. Masse revised strategy by Barry Warsaw """ from test.test_support import verbose,TestSkipped, import_module -import warnings -warnings.simplefilter('always') dl = import_module('dl', deprecated=True) -warnings.resetwarnings() sharedlibs = [ ('/usr/lib/libc.so', 'getpid'), From python-checkins at python.org Tue Aug 3 23:18:06 2010 From: python-checkins at python.org (mark.dickinson) Date: Tue, 3 Aug 2010 23:18:06 +0200 (CEST) Subject: [Python-checkins] r83688 - in python/branches/release26-maint: Lib/distutils/sysconfig.py Misc/NEWS Message-ID: <20100803211806.8B5FCEEA74@mail.python.org> Author: mark.dickinson Date: Tue Aug 3 23:18:06 2010 New Revision: 83688 Log: - Issue #8447: Make distutils.sysconfig follow symlinks in the path to the interpreter executable. This fixes a failure of test_httpservers on OS X. Modified: python/branches/release26-maint/Lib/distutils/sysconfig.py python/branches/release26-maint/Misc/NEWS Modified: python/branches/release26-maint/Lib/distutils/sysconfig.py ============================================================================== --- python/branches/release26-maint/Lib/distutils/sysconfig.py (original) +++ python/branches/release26-maint/Lib/distutils/sysconfig.py Tue Aug 3 23:18:06 2010 @@ -25,7 +25,7 @@ # Path to the base directory of the project. On Windows the binary may # live in project/PCBuild9. If we're dealing with an x64 Windows build, # it'll live in project/PCbuild/amd64. -project_base = os.path.dirname(os.path.abspath(sys.executable)) +project_base = os.path.dirname(os.path.realpath(sys.executable)) if os.name == "nt" and "pcbuild" in project_base[-8:].lower(): project_base = os.path.abspath(os.path.join(project_base, os.path.pardir)) # PC/VS7.1 @@ -74,7 +74,7 @@ if os.name == "posix": if python_build: - buildir = os.path.dirname(sys.executable) + buildir = os.path.dirname(os.path.realpath(sys.executable)) if plat_specific: # python.h is located in the buildir inc_dir = buildir @@ -222,7 +222,8 @@ def get_makefile_filename(): """Return full pathname of installed Makefile from the Python build.""" if python_build: - return os.path.join(os.path.dirname(sys.executable), "Makefile") + return os.path.join(os.path.dirname(os.path.realpath(sys.executable)), + "Makefile") lib_dir = get_python_lib(plat_specific=1, standard_lib=1) return os.path.join(lib_dir, "config", "Makefile") @@ -459,7 +460,7 @@ g['SO'] = '.pyd' g['EXE'] = ".exe" g['VERSION'] = get_python_version().replace(".", "") - g['BINDIR'] = os.path.dirname(os.path.abspath(sys.executable)) + g['BINDIR'] = os.path.dirname(os.path.realpath(sys.executable)) global _config_vars _config_vars = g Modified: python/branches/release26-maint/Misc/NEWS ============================================================================== --- python/branches/release26-maint/Misc/NEWS (original) +++ python/branches/release26-maint/Misc/NEWS Tue Aug 3 23:18:06 2010 @@ -89,6 +89,10 @@ Library ------- +- Issue #8447: Make distutils.sysconfig follow symlinks in the path to + the interpreter executable. This fixes a failure of test_httpservers + on OS X. + - Issue #7092: Fix the DeprecationWarnings emitted by the standard library when using the -3 flag. Patch by Florent Xicluna. From python-checkins at python.org Tue Aug 3 23:33:04 2010 From: python-checkins at python.org (mark.dickinson) Date: Tue, 3 Aug 2010 23:33:04 +0200 (CEST) Subject: [Python-checkins] r83689 - in python/branches/release31-maint: Lib/distutils/sysconfig.py Misc/NEWS Message-ID: <20100803213304.5DB94EEA43@mail.python.org> Author: mark.dickinson Date: Tue Aug 3 23:33:04 2010 New Revision: 83689 Log: Issue #8447: Make distutils.sysconfig follow symlinks in the path to the interpreter executable. This fixes a failure of test_httpservers on OS X. Modified: python/branches/release31-maint/Lib/distutils/sysconfig.py python/branches/release31-maint/Misc/NEWS Modified: python/branches/release31-maint/Lib/distutils/sysconfig.py ============================================================================== --- python/branches/release31-maint/Lib/distutils/sysconfig.py (original) +++ python/branches/release31-maint/Lib/distutils/sysconfig.py Tue Aug 3 23:33:04 2010 @@ -25,7 +25,7 @@ # Path to the base directory of the project. On Windows the binary may # live in project/PCBuild9. If we're dealing with an x64 Windows build, # it'll live in project/PCbuild/amd64. -project_base = os.path.dirname(os.path.abspath(sys.executable)) +project_base = os.path.dirname(os.path.realpath(sys.executable)) if os.name == "nt" and "pcbuild" in project_base[-8:].lower(): project_base = os.path.abspath(os.path.join(project_base, os.path.pardir)) # PC/VS7.1 @@ -77,7 +77,7 @@ # the build directory may not be the source directory, we # must use "srcdir" from the makefile to find the "Include" # directory. - base = os.path.dirname(os.path.abspath(sys.executable)) + base = os.path.dirname(os.path.realpath(sys.executable)) if plat_specific: return base else: @@ -223,7 +223,8 @@ def get_makefile_filename(): """Return full pathname of installed Makefile from the Python build.""" if python_build: - return os.path.join(os.path.dirname(sys.executable), "Makefile") + return os.path.join(os.path.dirname(os.path.realpath(sys.executable)), + "Makefile") lib_dir = get_python_lib(plat_specific=1, standard_lib=1) return os.path.join(lib_dir, "config", "Makefile") @@ -442,7 +443,7 @@ g['SO'] = '.pyd' g['EXE'] = ".exe" g['VERSION'] = get_python_version().replace(".", "") - g['BINDIR'] = os.path.dirname(os.path.abspath(sys.executable)) + g['BINDIR'] = os.path.dirname(os.path.realpath(sys.executable)) global _config_vars _config_vars = g Modified: python/branches/release31-maint/Misc/NEWS ============================================================================== --- python/branches/release31-maint/Misc/NEWS (original) +++ python/branches/release31-maint/Misc/NEWS Tue Aug 3 23:33:04 2010 @@ -84,6 +84,10 @@ Library ------- +- Issue #8447: Make distutils.sysconfig follow symlinks in the path to + the interpreter executable. This fixes a failure of test_httpservers + on OS X. + - Issue #7372: Fix pstats regression when stripping paths from profile data generated with the profile module. From python-checkins at python.org Wed Aug 4 00:14:10 2010 From: python-checkins at python.org (r.david.murray) Date: Wed, 4 Aug 2010 00:14:10 +0200 (CEST) Subject: [Python-checkins] r83690 - in python/branches/py3k: Lib/email/header.py Lib/email/test/test_email.py Misc/ACKS Misc/NEWS Message-ID: <20100803221410.58FF4EE9A0@mail.python.org> Author: r.david.murray Date: Wed Aug 4 00:14:10 2010 New Revision: 83690 Log: #3196: if needed pad a short base64 encoded word before trying to decode. The RFCs encourage following Postel's law: be liberal in what you accept. So if someone forgot to pad the base64 encoded word payload to an even four bytes, we add the padding before handing it to base64mime.decode. Previously, missing padding resulted in a HeaderParseError. Patch by Jason Williams. Modified: python/branches/py3k/Lib/email/header.py python/branches/py3k/Lib/email/test/test_email.py python/branches/py3k/Misc/ACKS python/branches/py3k/Misc/NEWS Modified: python/branches/py3k/Lib/email/header.py ============================================================================== --- python/branches/py3k/Lib/email/header.py (original) +++ python/branches/py3k/Lib/email/header.py Wed Aug 4 00:14:10 2010 @@ -94,6 +94,9 @@ word = email.quoprimime.header_decode(encoded_string) decoded_words.append((word, charset)) elif encoding == 'b': + paderr = len(encoded_string) % 4 # Postel's law: add missing padding + if paderr: + encoded_string += '==='[:4 - paderr] try: word = email.base64mime.decode(encoded_string) except binascii.Error: Modified: python/branches/py3k/Lib/email/test/test_email.py ============================================================================== --- python/branches/py3k/Lib/email/test/test_email.py (original) +++ python/branches/py3k/Lib/email/test/test_email.py Wed Aug 4 00:14:10 2010 @@ -1649,6 +1649,15 @@ (b'rg', None), (b'\xe5', 'iso-8859-1'), (b'sbord', None)]) + def test_rfc2047_B_bad_padding(self): + s = '=?iso-8859-1?B?%s?=' + data = [ # only test complete bytes + ('dm==', b'v'), ('dm=', b'v'), ('dm', b'v'), + ('dmk=', b'vi'), ('dmk', b'vi') + ] + for q, a in data: + dh = decode_header(s % q) + self.assertEqual(dh, [(a, 'iso-8859-1')]) # Test the MIMEMessage class @@ -3176,7 +3185,7 @@ def test_broken_base64_header(self): raises = self.assertRaises - s = 'Subject: =?EUC-KR?B?CSixpLDtKSC/7Liuvsax4iC6uLmwMcijIKHaILzSwd/H0SC8+LCjwLsgv7W/+Mj3IQ?=' + s = 'Subject: =?EUC-KR?B?CSixpLDtKSC/7Liuvsax4iC6uLmwMcijIKHaILzSwd/H0SC8+LCjwLsgv7W/+Mj3I ?=' raises(errors.HeaderParseError, decode_header, s) Modified: python/branches/py3k/Misc/ACKS ============================================================================== --- python/branches/py3k/Misc/ACKS (original) +++ python/branches/py3k/Misc/ACKS Wed Aug 4 00:14:10 2010 @@ -854,6 +854,7 @@ Gerry Wiener Frank Wierzbicki Bryce "Zooko" Wilcox-O'Hearn +Jason Williams John Williams Sue Williams Gerald S. Williams Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Wed Aug 4 00:14:10 2010 @@ -37,6 +37,9 @@ Library ------- +- Issue #3196: email header decoding is now forgiving if an RFC2047 + encoded word encoded in base64 is lacking padding. + - Issue #9444: Argparse now uses the first element of prefix_chars as the option character for the added 'h/help' option if prefix_chars does not contain a '-', instead of raising an error. From python-checkins at python.org Wed Aug 4 00:39:43 2010 From: python-checkins at python.org (barry.warsaw) Date: Wed, 4 Aug 2010 00:39:43 +0200 (CEST) Subject: [Python-checkins] r83691 - in python/branches/release26-maint: Include/patchlevel.h Lib/distutils/__init__.py Lib/idlelib/idlever.py Lib/pydoc_topics.py Misc/NEWS Misc/RPM/python-2.6.spec README Message-ID: <20100803223943.34D84EE992@mail.python.org> Author: barry.warsaw Date: Wed Aug 4 00:39:42 2010 New Revision: 83691 Log: Bumping to 2.6.6 rc 1 Modified: python/branches/release26-maint/Include/patchlevel.h python/branches/release26-maint/Lib/distutils/__init__.py python/branches/release26-maint/Lib/idlelib/idlever.py python/branches/release26-maint/Lib/pydoc_topics.py python/branches/release26-maint/Misc/NEWS python/branches/release26-maint/Misc/RPM/python-2.6.spec python/branches/release26-maint/README Modified: python/branches/release26-maint/Include/patchlevel.h ============================================================================== --- python/branches/release26-maint/Include/patchlevel.h (original) +++ python/branches/release26-maint/Include/patchlevel.h Wed Aug 4 00:39:42 2010 @@ -22,12 +22,12 @@ /*--start constants--*/ #define PY_MAJOR_VERSION 2 #define PY_MINOR_VERSION 6 -#define PY_MICRO_VERSION 5 -#define PY_RELEASE_LEVEL PY_RELEASE_LEVEL_FINAL -#define PY_RELEASE_SERIAL 0 +#define PY_MICRO_VERSION 6 +#define PY_RELEASE_LEVEL PY_RELEASE_LEVEL_GAMMA +#define PY_RELEASE_SERIAL 1 /* Version as a string */ -#define PY_VERSION "2.6.5+" +#define PY_VERSION "2.6.6rc1" /*--end constants--*/ /* Subversion Revision number of this file (not of the repository) */ Modified: python/branches/release26-maint/Lib/distutils/__init__.py ============================================================================== --- python/branches/release26-maint/Lib/distutils/__init__.py (original) +++ python/branches/release26-maint/Lib/distutils/__init__.py Wed Aug 4 00:39:42 2010 @@ -22,5 +22,5 @@ # #--start constants-- -__version__ = "2.6.5" +__version__ = "2.6.6rc1" #--end constants-- Modified: python/branches/release26-maint/Lib/idlelib/idlever.py ============================================================================== --- python/branches/release26-maint/Lib/idlelib/idlever.py (original) +++ python/branches/release26-maint/Lib/idlelib/idlever.py Wed Aug 4 00:39:42 2010 @@ -1 +1 @@ -IDLE_VERSION = "2.6.5" +IDLE_VERSION = "2.6.6rc1" Modified: python/branches/release26-maint/Lib/pydoc_topics.py ============================================================================== --- python/branches/release26-maint/Lib/pydoc_topics.py (original) +++ python/branches/release26-maint/Lib/pydoc_topics.py Wed Aug 4 00:39:42 2010 @@ -1,9 +1,9 @@ -# Autogenerated by Sphinx on Thu Mar 18 18:09:37 2010 +# Autogenerated by Sphinx on Tue Aug 3 18:26:52 2010 topics = {'assert': u'\nThe ``assert`` statement\n************************\n\nAssert statements are a convenient way to insert debugging assertions\ninto a program:\n\n assert_stmt ::= "assert" expression ["," expression]\n\nThe simple form, ``assert expression``, is equivalent to\n\n if __debug__:\n if not expression: raise AssertionError\n\nThe extended form, ``assert expression1, expression2``, is equivalent\nto\n\n if __debug__:\n if not expression1: raise AssertionError(expression2)\n\nThese equivalences assume that ``__debug__`` and ``AssertionError``\nrefer to the built-in variables with those names. In the current\nimplementation, the built-in variable ``__debug__`` is ``True`` under\nnormal circumstances, ``False`` when optimization is requested\n(command line option -O). The current code generator emits no code\nfor an assert statement when optimization is requested at compile\ntime. Note that it is unnecessary to include the source code for the\nexpression that failed in the error message; it will be displayed as\npart of the stack trace.\n\nAssignments to ``__debug__`` are illegal. The value for the built-in\nvariable is determined when the interpreter starts.\n', 'assignment': u'\nAssignment statements\n*********************\n\nAssignment statements are used to (re)bind names to values and to\nmodify attributes or items of mutable objects:\n\n assignment_stmt ::= (target_list "=")+ (expression_list | yield_expression)\n target_list ::= target ("," target)* [","]\n target ::= identifier\n | "(" target_list ")"\n | "[" target_list "]"\n | attributeref\n | subscription\n | slicing\n\n(See section *Primaries* for the syntax definitions for the last three\nsymbols.)\n\nAn assignment statement evaluates the expression list (remember that\nthis can be a single expression or a comma-separated list, the latter\nyielding a tuple) and assigns the single resulting object to each of\nthe target lists, from left to right.\n\nAssignment is defined recursively depending on the form of the target\n(list). When a target is part of a mutable object (an attribute\nreference, subscription or slicing), the mutable object must\nultimately perform the assignment and decide about its validity, and\nmay raise an exception if the assignment is unacceptable. The rules\nobserved by various types and the exceptions raised are given with the\ndefinition of the object types (see section *The standard type\nhierarchy*).\n\nAssignment of an object to a target list is recursively defined as\nfollows.\n\n* If the target list is a single target: The object is assigned to\n that target.\n\n* If the target list is a comma-separated list of targets: The object\n must be an iterable with the same number of items as there are\n targets in the target list, and the items are assigned, from left to\n right, to the corresponding targets. (This rule is relaxed as of\n Python 1.5; in earlier versions, the object had to be a tuple.\n Since strings are sequences, an assignment like ``a, b = "xy"`` is\n now legal as long as the string has the right length.)\n\nAssignment of an object to a single target is recursively defined as\nfollows.\n\n* If the target is an identifier (name):\n\n * If the name does not occur in a ``global`` statement in the\n current code block: the name is bound to the object in the current\n local namespace.\n\n * Otherwise: the name is bound to the object in the current global\n namespace.\n\n The name is rebound if it was already bound. This may cause the\n reference count for the object previously bound to the name to reach\n zero, causing the object to be deallocated and its destructor (if it\n has one) to be called.\n\n* If the target is a target list enclosed in parentheses or in square\n brackets: The object must be an iterable with the same number of\n items as there are targets in the target list, and its items are\n assigned, from left to right, to the corresponding targets.\n\n* If the target is an attribute reference: The primary expression in\n the reference is evaluated. It should yield an object with\n assignable attributes; if this is not the case, ``TypeError`` is\n raised. That object is then asked to assign the assigned object to\n the given attribute; if it cannot perform the assignment, it raises\n an exception (usually but not necessarily ``AttributeError``).\n\n Note: If the object is a class instance and the attribute reference\n occurs on both sides of the assignment operator, the RHS expression,\n ``a.x`` can access either an instance attribute or (if no instance\n attribute exists) a class attribute. The LHS target ``a.x`` is\n always set as an instance attribute, creating it if necessary.\n Thus, the two occurrences of ``a.x`` do not necessarily refer to the\n same attribute: if the RHS expression refers to a class attribute,\n the LHS creates a new instance attribute as the target of the\n assignment:\n\n class Cls:\n x = 3 # class variable\n inst = Cls()\n inst.x = inst.x + 1 # writes inst.x as 4 leaving Cls.x as 3\n\n This description does not necessarily apply to descriptor\n attributes, such as properties created with ``property()``.\n\n* If the target is a subscription: The primary expression in the\n reference is evaluated. It should yield either a mutable sequence\n object (such as a list) or a mapping object (such as a dictionary).\n Next, the subscript expression is evaluated.\n\n If the primary is a mutable sequence object (such as a list), the\n subscript must yield a plain integer. If it is negative, the\n sequence\'s length is added to it. The resulting value must be a\n nonnegative integer less than the sequence\'s length, and the\n sequence is asked to assign the assigned object to its item with\n that index. If the index is out of range, ``IndexError`` is raised\n (assignment to a subscripted sequence cannot add new items to a\n list).\n\n If the primary is a mapping object (such as a dictionary), the\n subscript must have a type compatible with the mapping\'s key type,\n and the mapping is then asked to create a key/datum pair which maps\n the subscript to the assigned object. This can either replace an\n existing key/value pair with the same key value, or insert a new\n key/value pair (if no key with the same value existed).\n\n* If the target is a slicing: The primary expression in the reference\n is evaluated. It should yield a mutable sequence object (such as a\n list). The assigned object should be a sequence object of the same\n type. Next, the lower and upper bound expressions are evaluated,\n insofar they are present; defaults are zero and the sequence\'s\n length. The bounds should evaluate to (small) integers. If either\n bound is negative, the sequence\'s length is added to it. The\n resulting bounds are clipped to lie between zero and the sequence\'s\n length, inclusive. Finally, the sequence object is asked to replace\n the slice with the items of the assigned sequence. The length of\n the slice may be different from the length of the assigned sequence,\n thus changing the length of the target sequence, if the object\n allows it.\n\n**CPython implementation detail:** In the current implementation, the\nsyntax for targets is taken to be the same as for expressions, and\ninvalid syntax is rejected during the code generation phase, causing\nless detailed error messages.\n\nWARNING: Although the definition of assignment implies that overlaps\nbetween the left-hand side and the right-hand side are \'safe\' (for\nexample ``a, b = b, a`` swaps two variables), overlaps *within* the\ncollection of assigned-to variables are not safe! For instance, the\nfollowing program prints ``[0, 2]``:\n\n x = [0, 1]\n i = 0\n i, x[i] = 1, 2\n print x\n\n\nAugmented assignment statements\n===============================\n\nAugmented assignment is the combination, in a single statement, of a\nbinary operation and an assignment statement:\n\n augmented_assignment_stmt ::= augtarget augop (expression_list | yield_expression)\n augtarget ::= identifier | attributeref | subscription | slicing\n augop ::= "+=" | "-=" | "*=" | "/=" | "//=" | "%=" | "**="\n | ">>=" | "<<=" | "&=" | "^=" | "|="\n\n(See section *Primaries* for the syntax definitions for the last three\nsymbols.)\n\nAn augmented assignment evaluates the target (which, unlike normal\nassignment statements, cannot be an unpacking) and the expression\nlist, performs the binary operation specific to the type of assignment\non the two operands, and assigns the result to the original target.\nThe target is only evaluated once.\n\nAn augmented assignment expression like ``x += 1`` can be rewritten as\n``x = x + 1`` to achieve a similar, but not exactly equal effect. In\nthe augmented version, ``x`` is only evaluated once. Also, when\npossible, the actual operation is performed *in-place*, meaning that\nrather than creating a new object and assigning that to the target,\nthe old object is modified instead.\n\nWith the exception of assigning to tuples and multiple targets in a\nsingle statement, the assignment done by augmented assignment\nstatements is handled the same way as normal assignments. Similarly,\nwith the exception of the possible *in-place* behavior, the binary\noperation performed by augmented assignment is the same as the normal\nbinary operations.\n\nFor targets which are attribute references, the same *caveat about\nclass and instance attributes* applies as for regular assignments.\n', 'atom-identifiers': u'\nIdentifiers (Names)\n*******************\n\nAn identifier occurring as an atom is a name. See section\n*Identifiers and keywords* for lexical definition and section *Naming\nand binding* for documentation of naming and binding.\n\nWhen the name is bound to an object, evaluation of the atom yields\nthat object. When a name is not bound, an attempt to evaluate it\nraises a ``NameError`` exception.\n\n**Private name mangling:** When an identifier that textually occurs in\na class definition begins with two or more underscore characters and\ndoes not end in two or more underscores, it is considered a *private\nname* of that class. Private names are transformed to a longer form\nbefore code is generated for them. The transformation inserts the\nclass name in front of the name, with leading underscores removed, and\na single underscore inserted in front of the class name. For example,\nthe identifier ``__spam`` occurring in a class named ``Ham`` will be\ntransformed to ``_Ham__spam``. This transformation is independent of\nthe syntactical context in which the identifier is used. If the\ntransformed name is extremely long (longer than 255 characters),\nimplementation defined truncation may happen. If the class name\nconsists only of underscores, no transformation is done.\n', 'atom-literals': u"\nLiterals\n********\n\nPython supports string literals and various numeric literals:\n\n literal ::= stringliteral | integer | longinteger\n | floatnumber | imagnumber\n\nEvaluation of a literal yields an object of the given type (string,\ninteger, long integer, floating point number, complex number) with the\ngiven value. The value may be approximated in the case of floating\npoint and imaginary (complex) literals. See section *Literals* for\ndetails.\n\nAll literals correspond to immutable data types, and hence the\nobject's identity is less important than its value. Multiple\nevaluations of literals with the same value (either the same\noccurrence in the program text or a different occurrence) may obtain\nthe same object or a different object with the same value.\n", - 'attribute-access': u'\nCustomizing attribute access\n****************************\n\nThe following methods can be defined to customize the meaning of\nattribute access (use of, assignment to, or deletion of ``x.name``)\nfor class instances.\n\nobject.__getattr__(self, name)\n\n Called when an attribute lookup has not found the attribute in the\n usual places (i.e. it is not an instance attribute nor is it found\n in the class tree for ``self``). ``name`` is the attribute name.\n This method should return the (computed) attribute value or raise\n an ``AttributeError`` exception.\n\n Note that if the attribute is found through the normal mechanism,\n ``__getattr__()`` is not called. (This is an intentional asymmetry\n between ``__getattr__()`` and ``__setattr__()``.) This is done both\n for efficiency reasons and because otherwise ``__getattr__()``\n would have no way to access other attributes of the instance. Note\n that at least for instance variables, you can fake total control by\n not inserting any values in the instance attribute dictionary (but\n instead inserting them in another object). See the\n ``__getattribute__()`` method below for a way to actually get total\n control in new-style classes.\n\nobject.__setattr__(self, name, value)\n\n Called when an attribute assignment is attempted. This is called\n instead of the normal mechanism (i.e. store the value in the\n instance dictionary). *name* is the attribute name, *value* is the\n value to be assigned to it.\n\n If ``__setattr__()`` wants to assign to an instance attribute, it\n should not simply execute ``self.name = value`` --- this would\n cause a recursive call to itself. Instead, it should insert the\n value in the dictionary of instance attributes, e.g.,\n ``self.__dict__[name] = value``. For new-style classes, rather\n than accessing the instance dictionary, it should call the base\n class method with the same name, for example,\n ``object.__setattr__(self, name, value)``.\n\nobject.__delattr__(self, name)\n\n Like ``__setattr__()`` but for attribute deletion instead of\n assignment. This should only be implemented if ``del obj.name`` is\n meaningful for the object.\n\n\nMore attribute access for new-style classes\n===========================================\n\nThe following methods only apply to new-style classes.\n\nobject.__getattribute__(self, name)\n\n Called unconditionally to implement attribute accesses for\n instances of the class. If the class also defines\n ``__getattr__()``, the latter will not be called unless\n ``__getattribute__()`` either calls it explicitly or raises an\n ``AttributeError``. This method should return the (computed)\n attribute value or raise an ``AttributeError`` exception. In order\n to avoid infinite recursion in this method, its implementation\n should always call the base class method with the same name to\n access any attributes it needs, for example,\n ``object.__getattribute__(self, name)``.\n\n Note: This method may still be bypassed when looking up special methods\n as the result of implicit invocation via language syntax or\n built-in functions. See *Special method lookup for new-style\n classes*.\n\n\nImplementing Descriptors\n========================\n\nThe following methods only apply when an instance of the class\ncontaining the method (a so-called *descriptor* class) appears in the\nclass dictionary of another new-style class, known as the *owner*\nclass. In the examples below, "the attribute" refers to the attribute\nwhose name is the key of the property in the owner class\'\n``__dict__``. Descriptors can only be implemented as new-style\nclasses themselves.\n\nobject.__get__(self, instance, owner)\n\n Called to get the attribute of the owner class (class attribute\n access) or of an instance of that class (instance attribute\n access). *owner* is always the owner class, while *instance* is the\n instance that the attribute was accessed through, or ``None`` when\n the attribute is accessed through the *owner*. This method should\n return the (computed) attribute value or raise an\n ``AttributeError`` exception.\n\nobject.__set__(self, instance, value)\n\n Called to set the attribute on an instance *instance* of the owner\n class to a new value, *value*.\n\nobject.__delete__(self, instance)\n\n Called to delete the attribute on an instance *instance* of the\n owner class.\n\n\nInvoking Descriptors\n====================\n\nIn general, a descriptor is an object attribute with "binding\nbehavior", one whose attribute access has been overridden by methods\nin the descriptor protocol: ``__get__()``, ``__set__()``, and\n``__delete__()``. If any of those methods are defined for an object,\nit is said to be a descriptor.\n\nThe default behavior for attribute access is to get, set, or delete\nthe attribute from an object\'s dictionary. For instance, ``a.x`` has a\nlookup chain starting with ``a.__dict__[\'x\']``, then\n``type(a).__dict__[\'x\']``, and continuing through the base classes of\n``type(a)`` excluding metaclasses.\n\nHowever, if the looked-up value is an object defining one of the\ndescriptor methods, then Python may override the default behavior and\ninvoke the descriptor method instead. Where this occurs in the\nprecedence chain depends on which descriptor methods were defined and\nhow they were called. Note that descriptors are only invoked for new\nstyle objects or classes (ones that subclass ``object()`` or\n``type()``).\n\nThe starting point for descriptor invocation is a binding, ``a.x``.\nHow the arguments are assembled depends on ``a``:\n\nDirect Call\n The simplest and least common call is when user code directly\n invokes a descriptor method: ``x.__get__(a)``.\n\nInstance Binding\n If binding to a new-style object instance, ``a.x`` is transformed\n into the call: ``type(a).__dict__[\'x\'].__get__(a, type(a))``.\n\nClass Binding\n If binding to a new-style class, ``A.x`` is transformed into the\n call: ``A.__dict__[\'x\'].__get__(None, A)``.\n\nSuper Binding\n If ``a`` is an instance of ``super``, then the binding ``super(B,\n obj).m()`` searches ``obj.__class__.__mro__`` for the base class\n ``A`` immediately preceding ``B`` and then invokes the descriptor\n with the call: ``A.__dict__[\'m\'].__get__(obj, A)``.\n\nFor instance bindings, the precedence of descriptor invocation depends\non the which descriptor methods are defined. Normally, data\ndescriptors define both ``__get__()`` and ``__set__()``, while non-\ndata descriptors have just the ``__get__()`` method. Data descriptors\nalways override a redefinition in an instance dictionary. In\ncontrast, non-data descriptors can be overridden by instances. [2]\n\nPython methods (including ``staticmethod()`` and ``classmethod()``)\nare implemented as non-data descriptors. Accordingly, instances can\nredefine and override methods. This allows individual instances to\nacquire behaviors that differ from other instances of the same class.\n\nThe ``property()`` function is implemented as a data descriptor.\nAccordingly, instances cannot override the behavior of a property.\n\n\n__slots__\n=========\n\nBy default, instances of both old and new-style classes have a\ndictionary for attribute storage. This wastes space for objects\nhaving very few instance variables. The space consumption can become\nacute when creating large numbers of instances.\n\nThe default can be overridden by defining *__slots__* in a new-style\nclass definition. The *__slots__* declaration takes a sequence of\ninstance variables and reserves just enough space in each instance to\nhold a value for each variable. Space is saved because *__dict__* is\nnot created for each instance.\n\n__slots__\n\n This class variable can be assigned a string, iterable, or sequence\n of strings with variable names used by instances. If defined in a\n new-style class, *__slots__* reserves space for the declared\n variables and prevents the automatic creation of *__dict__* and\n *__weakref__* for each instance.\n\n New in version 2.2.\n\nNotes on using *__slots__*\n\n* When inheriting from a class without *__slots__*, the *__dict__*\n attribute of that class will always be accessible, so a *__slots__*\n definition in the subclass is meaningless.\n\n* Without a *__dict__* variable, instances cannot be assigned new\n variables not listed in the *__slots__* definition. Attempts to\n assign to an unlisted variable name raises ``AttributeError``. If\n dynamic assignment of new variables is desired, then add\n ``\'__dict__\'`` to the sequence of strings in the *__slots__*\n declaration.\n\n Changed in version 2.3: Previously, adding ``\'__dict__\'`` to the\n *__slots__* declaration would not enable the assignment of new\n attributes not specifically listed in the sequence of instance\n variable names.\n\n* Without a *__weakref__* variable for each instance, classes defining\n *__slots__* do not support weak references to its instances. If weak\n reference support is needed, then add ``\'__weakref__\'`` to the\n sequence of strings in the *__slots__* declaration.\n\n Changed in version 2.3: Previously, adding ``\'__weakref__\'`` to the\n *__slots__* declaration would not enable support for weak\n references.\n\n* *__slots__* are implemented at the class level by creating\n descriptors (*Implementing Descriptors*) for each variable name. As\n a result, class attributes cannot be used to set default values for\n instance variables defined by *__slots__*; otherwise, the class\n attribute would overwrite the descriptor assignment.\n\n* The action of a *__slots__* declaration is limited to the class\n where it is defined. As a result, subclasses will have a *__dict__*\n unless they also define *__slots__* (which must only contain names\n of any *additional* slots).\n\n* If a class defines a slot also defined in a base class, the instance\n variable defined by the base class slot is inaccessible (except by\n retrieving its descriptor directly from the base class). This\n renders the meaning of the program undefined. In the future, a\n check may be added to prevent this.\n\n* Nonempty *__slots__* does not work for classes derived from\n "variable-length" built-in types such as ``long``, ``str`` and\n ``tuple``.\n\n* Any non-string iterable may be assigned to *__slots__*. Mappings may\n also be used; however, in the future, special meaning may be\n assigned to the values corresponding to each key.\n\n* *__class__* assignment works only if both classes have the same\n *__slots__*.\n\n Changed in version 2.6: Previously, *__class__* assignment raised an\n error if either new or old class had *__slots__*.\n', + 'attribute-access': u'\nCustomizing attribute access\n****************************\n\nThe following methods can be defined to customize the meaning of\nattribute access (use of, assignment to, or deletion of ``x.name``)\nfor class instances.\n\nobject.__getattr__(self, name)\n\n Called when an attribute lookup has not found the attribute in the\n usual places (i.e. it is not an instance attribute nor is it found\n in the class tree for ``self``). ``name`` is the attribute name.\n This method should return the (computed) attribute value or raise\n an ``AttributeError`` exception.\n\n Note that if the attribute is found through the normal mechanism,\n ``__getattr__()`` is not called. (This is an intentional asymmetry\n between ``__getattr__()`` and ``__setattr__()``.) This is done both\n for efficiency reasons and because otherwise ``__getattr__()``\n would have no way to access other attributes of the instance. Note\n that at least for instance variables, you can fake total control by\n not inserting any values in the instance attribute dictionary (but\n instead inserting them in another object). See the\n ``__getattribute__()`` method below for a way to actually get total\n control in new-style classes.\n\nobject.__setattr__(self, name, value)\n\n Called when an attribute assignment is attempted. This is called\n instead of the normal mechanism (i.e. store the value in the\n instance dictionary). *name* is the attribute name, *value* is the\n value to be assigned to it.\n\n If ``__setattr__()`` wants to assign to an instance attribute, it\n should not simply execute ``self.name = value`` --- this would\n cause a recursive call to itself. Instead, it should insert the\n value in the dictionary of instance attributes, e.g.,\n ``self.__dict__[name] = value``. For new-style classes, rather\n than accessing the instance dictionary, it should call the base\n class method with the same name, for example,\n ``object.__setattr__(self, name, value)``.\n\nobject.__delattr__(self, name)\n\n Like ``__setattr__()`` but for attribute deletion instead of\n assignment. This should only be implemented if ``del obj.name`` is\n meaningful for the object.\n\n\nMore attribute access for new-style classes\n===========================================\n\nThe following methods only apply to new-style classes.\n\nobject.__getattribute__(self, name)\n\n Called unconditionally to implement attribute accesses for\n instances of the class. If the class also defines\n ``__getattr__()``, the latter will not be called unless\n ``__getattribute__()`` either calls it explicitly or raises an\n ``AttributeError``. This method should return the (computed)\n attribute value or raise an ``AttributeError`` exception. In order\n to avoid infinite recursion in this method, its implementation\n should always call the base class method with the same name to\n access any attributes it needs, for example,\n ``object.__getattribute__(self, name)``.\n\n Note: This method may still be bypassed when looking up special methods\n as the result of implicit invocation via language syntax or\n built-in functions. See *Special method lookup for new-style\n classes*.\n\n\nImplementing Descriptors\n========================\n\nThe following methods only apply when an instance of the class\ncontaining the method (a so-called *descriptor* class) appears in the\nclass dictionary of another new-style class, known as the *owner*\nclass. In the examples below, "the attribute" refers to the attribute\nwhose name is the key of the property in the owner class\'\n``__dict__``. Descriptors can only be implemented as new-style\nclasses themselves.\n\nobject.__get__(self, instance, owner)\n\n Called to get the attribute of the owner class (class attribute\n access) or of an instance of that class (instance attribute\n access). *owner* is always the owner class, while *instance* is the\n instance that the attribute was accessed through, or ``None`` when\n the attribute is accessed through the *owner*. This method should\n return the (computed) attribute value or raise an\n ``AttributeError`` exception.\n\nobject.__set__(self, instance, value)\n\n Called to set the attribute on an instance *instance* of the owner\n class to a new value, *value*.\n\nobject.__delete__(self, instance)\n\n Called to delete the attribute on an instance *instance* of the\n owner class.\n\n\nInvoking Descriptors\n====================\n\nIn general, a descriptor is an object attribute with "binding\nbehavior", one whose attribute access has been overridden by methods\nin the descriptor protocol: ``__get__()``, ``__set__()``, and\n``__delete__()``. If any of those methods are defined for an object,\nit is said to be a descriptor.\n\nThe default behavior for attribute access is to get, set, or delete\nthe attribute from an object\'s dictionary. For instance, ``a.x`` has a\nlookup chain starting with ``a.__dict__[\'x\']``, then\n``type(a).__dict__[\'x\']``, and continuing through the base classes of\n``type(a)`` excluding metaclasses.\n\nHowever, if the looked-up value is an object defining one of the\ndescriptor methods, then Python may override the default behavior and\ninvoke the descriptor method instead. Where this occurs in the\nprecedence chain depends on which descriptor methods were defined and\nhow they were called. Note that descriptors are only invoked for new\nstyle objects or classes (ones that subclass ``object()`` or\n``type()``).\n\nThe starting point for descriptor invocation is a binding, ``a.x``.\nHow the arguments are assembled depends on ``a``:\n\nDirect Call\n The simplest and least common call is when user code directly\n invokes a descriptor method: ``x.__get__(a)``.\n\nInstance Binding\n If binding to a new-style object instance, ``a.x`` is transformed\n into the call: ``type(a).__dict__[\'x\'].__get__(a, type(a))``.\n\nClass Binding\n If binding to a new-style class, ``A.x`` is transformed into the\n call: ``A.__dict__[\'x\'].__get__(None, A)``.\n\nSuper Binding\n If ``a`` is an instance of ``super``, then the binding ``super(B,\n obj).m()`` searches ``obj.__class__.__mro__`` for the base class\n ``A`` immediately preceding ``B`` and then invokes the descriptor\n with the call: ``A.__dict__[\'m\'].__get__(obj, A)``.\n\nFor instance bindings, the precedence of descriptor invocation depends\non the which descriptor methods are defined. A descriptor can define\nany combination of ``__get__()``, ``__set__()`` and ``__delete__()``.\nIf it does not define ``__get__()``, then accessing the attribute will\nreturn the descriptor object itself unless there is a value in the\nobject\'s instance dictionary. If the descriptor defines ``__set__()``\nand/or ``__delete__()``, it is a data descriptor; if it defines\nneither, it is a non-data descriptor. Normally, data descriptors\ndefine both ``__get__()`` and ``__set__()``, while non-data\ndescriptors have just the ``__get__()`` method. Data descriptors with\n``__set__()`` and ``__get__()`` defined always override a redefinition\nin an instance dictionary. In contrast, non-data descriptors can be\noverridden by instances.\n\nPython methods (including ``staticmethod()`` and ``classmethod()``)\nare implemented as non-data descriptors. Accordingly, instances can\nredefine and override methods. This allows individual instances to\nacquire behaviors that differ from other instances of the same class.\n\nThe ``property()`` function is implemented as a data descriptor.\nAccordingly, instances cannot override the behavior of a property.\n\n\n__slots__\n=========\n\nBy default, instances of both old and new-style classes have a\ndictionary for attribute storage. This wastes space for objects\nhaving very few instance variables. The space consumption can become\nacute when creating large numbers of instances.\n\nThe default can be overridden by defining *__slots__* in a new-style\nclass definition. The *__slots__* declaration takes a sequence of\ninstance variables and reserves just enough space in each instance to\nhold a value for each variable. Space is saved because *__dict__* is\nnot created for each instance.\n\n__slots__\n\n This class variable can be assigned a string, iterable, or sequence\n of strings with variable names used by instances. If defined in a\n new-style class, *__slots__* reserves space for the declared\n variables and prevents the automatic creation of *__dict__* and\n *__weakref__* for each instance.\n\n New in version 2.2.\n\nNotes on using *__slots__*\n\n* When inheriting from a class without *__slots__*, the *__dict__*\n attribute of that class will always be accessible, so a *__slots__*\n definition in the subclass is meaningless.\n\n* Without a *__dict__* variable, instances cannot be assigned new\n variables not listed in the *__slots__* definition. Attempts to\n assign to an unlisted variable name raises ``AttributeError``. If\n dynamic assignment of new variables is desired, then add\n ``\'__dict__\'`` to the sequence of strings in the *__slots__*\n declaration.\n\n Changed in version 2.3: Previously, adding ``\'__dict__\'`` to the\n *__slots__* declaration would not enable the assignment of new\n attributes not specifically listed in the sequence of instance\n variable names.\n\n* Without a *__weakref__* variable for each instance, classes defining\n *__slots__* do not support weak references to its instances. If weak\n reference support is needed, then add ``\'__weakref__\'`` to the\n sequence of strings in the *__slots__* declaration.\n\n Changed in version 2.3: Previously, adding ``\'__weakref__\'`` to the\n *__slots__* declaration would not enable support for weak\n references.\n\n* *__slots__* are implemented at the class level by creating\n descriptors (*Implementing Descriptors*) for each variable name. As\n a result, class attributes cannot be used to set default values for\n instance variables defined by *__slots__*; otherwise, the class\n attribute would overwrite the descriptor assignment.\n\n* The action of a *__slots__* declaration is limited to the class\n where it is defined. As a result, subclasses will have a *__dict__*\n unless they also define *__slots__* (which must only contain names\n of any *additional* slots).\n\n* If a class defines a slot also defined in a base class, the instance\n variable defined by the base class slot is inaccessible (except by\n retrieving its descriptor directly from the base class). This\n renders the meaning of the program undefined. In the future, a\n check may be added to prevent this.\n\n* Nonempty *__slots__* does not work for classes derived from\n "variable-length" built-in types such as ``long``, ``str`` and\n ``tuple``.\n\n* Any non-string iterable may be assigned to *__slots__*. Mappings may\n also be used; however, in the future, special meaning may be\n assigned to the values corresponding to each key.\n\n* *__class__* assignment works only if both classes have the same\n *__slots__*.\n\n Changed in version 2.6: Previously, *__class__* assignment raised an\n error if either new or old class had *__slots__*.\n', 'attribute-references': u'\nAttribute references\n********************\n\nAn attribute reference is a primary followed by a period and a name:\n\n attributeref ::= primary "." identifier\n\nThe primary must evaluate to an object of a type that supports\nattribute references, e.g., a module, list, or an instance. This\nobject is then asked to produce the attribute whose name is the\nidentifier. If this attribute is not available, the exception\n``AttributeError`` is raised. Otherwise, the type and value of the\nobject produced is determined by the object. Multiple evaluations of\nthe same attribute reference may yield different objects.\n', 'augassign': u'\nAugmented assignment statements\n*******************************\n\nAugmented assignment is the combination, in a single statement, of a\nbinary operation and an assignment statement:\n\n augmented_assignment_stmt ::= augtarget augop (expression_list | yield_expression)\n augtarget ::= identifier | attributeref | subscription | slicing\n augop ::= "+=" | "-=" | "*=" | "/=" | "//=" | "%=" | "**="\n | ">>=" | "<<=" | "&=" | "^=" | "|="\n\n(See section *Primaries* for the syntax definitions for the last three\nsymbols.)\n\nAn augmented assignment evaluates the target (which, unlike normal\nassignment statements, cannot be an unpacking) and the expression\nlist, performs the binary operation specific to the type of assignment\non the two operands, and assigns the result to the original target.\nThe target is only evaluated once.\n\nAn augmented assignment expression like ``x += 1`` can be rewritten as\n``x = x + 1`` to achieve a similar, but not exactly equal effect. In\nthe augmented version, ``x`` is only evaluated once. Also, when\npossible, the actual operation is performed *in-place*, meaning that\nrather than creating a new object and assigning that to the target,\nthe old object is modified instead.\n\nWith the exception of assigning to tuples and multiple targets in a\nsingle statement, the assignment done by augmented assignment\nstatements is handled the same way as normal assignments. Similarly,\nwith the exception of the possible *in-place* behavior, the binary\noperation performed by augmented assignment is the same as the normal\nbinary operations.\n\nFor targets which are attribute references, the same *caveat about\nclass and instance attributes* applies as for regular assignments.\n', 'binary': u'\nBinary arithmetic operations\n****************************\n\nThe binary arithmetic operations have the conventional priority\nlevels. Note that some of these operations also apply to certain non-\nnumeric types. Apart from the power operator, there are only two\nlevels, one for multiplicative operators and one for additive\noperators:\n\n m_expr ::= u_expr | m_expr "*" u_expr | m_expr "//" u_expr | m_expr "/" u_expr\n | m_expr "%" u_expr\n a_expr ::= m_expr | a_expr "+" m_expr | a_expr "-" m_expr\n\nThe ``*`` (multiplication) operator yields the product of its\narguments. The arguments must either both be numbers, or one argument\nmust be an integer (plain or long) and the other must be a sequence.\nIn the former case, the numbers are converted to a common type and\nthen multiplied together. In the latter case, sequence repetition is\nperformed; a negative repetition factor yields an empty sequence.\n\nThe ``/`` (division) and ``//`` (floor division) operators yield the\nquotient of their arguments. The numeric arguments are first\nconverted to a common type. Plain or long integer division yields an\ninteger of the same type; the result is that of mathematical division\nwith the \'floor\' function applied to the result. Division by zero\nraises the ``ZeroDivisionError`` exception.\n\nThe ``%`` (modulo) operator yields the remainder from the division of\nthe first argument by the second. The numeric arguments are first\nconverted to a common type. A zero right argument raises the\n``ZeroDivisionError`` exception. The arguments may be floating point\nnumbers, e.g., ``3.14%0.7`` equals ``0.34`` (since ``3.14`` equals\n``4*0.7 + 0.34``.) The modulo operator always yields a result with\nthe same sign as its second operand (or zero); the absolute value of\nthe result is strictly smaller than the absolute value of the second\noperand [2].\n\nThe integer division and modulo operators are connected by the\nfollowing identity: ``x == (x/y)*y + (x%y)``. Integer division and\nmodulo are also connected with the built-in function ``divmod()``:\n``divmod(x, y) == (x/y, x%y)``. These identities don\'t hold for\nfloating point numbers; there similar identities hold approximately\nwhere ``x/y`` is replaced by ``floor(x/y)`` or ``floor(x/y) - 1`` [3].\n\nIn addition to performing the modulo operation on numbers, the ``%``\noperator is also overloaded by string and unicode objects to perform\nstring formatting (also known as interpolation). The syntax for string\nformatting is described in the Python Library Reference, section\n*String Formatting Operations*.\n\nDeprecated since version 2.3: The floor division operator, the modulo\noperator, and the ``divmod()`` function are no longer defined for\ncomplex numbers. Instead, convert to a floating point number using\nthe ``abs()`` function if appropriate.\n\nThe ``+`` (addition) operator yields the sum of its arguments. The\narguments must either both be numbers or both sequences of the same\ntype. In the former case, the numbers are converted to a common type\nand then added together. In the latter case, the sequences are\nconcatenated.\n\nThe ``-`` (subtraction) operator yields the difference of its\narguments. The numeric arguments are first converted to a common\ntype.\n', @@ -13,7 +13,7 @@ 'bltin-file-objects': u'\nFile Objects\n************\n\nFile objects are implemented using C\'s ``stdio`` package and can be\ncreated with the built-in ``open()`` function. File objects are also\nreturned by some other built-in functions and methods, such as\n``os.popen()`` and ``os.fdopen()`` and the ``makefile()`` method of\nsocket objects. Temporary files can be created using the ``tempfile``\nmodule, and high-level file operations such as copying, moving, and\ndeleting files and directories can be achieved with the ``shutil``\nmodule.\n\nWhen a file operation fails for an I/O-related reason, the exception\n``IOError`` is raised. This includes situations where the operation\nis not defined for some reason, like ``seek()`` on a tty device or\nwriting a file opened for reading.\n\nFiles have the following methods:\n\nfile.close()\n\n Close the file. A closed file cannot be read or written any more.\n Any operation which requires that the file be open will raise a\n ``ValueError`` after the file has been closed. Calling ``close()``\n more than once is allowed.\n\n As of Python 2.5, you can avoid having to call this method\n explicitly if you use the ``with`` statement. For example, the\n following code will automatically close *f* when the ``with`` block\n is exited:\n\n from __future__ import with_statement # This isn\'t required in Python 2.6\n\n with open("hello.txt") as f:\n for line in f:\n print line\n\n In older versions of Python, you would have needed to do this to\n get the same effect:\n\n f = open("hello.txt")\n try:\n for line in f:\n print line\n finally:\n f.close()\n\n Note: Not all "file-like" types in Python support use as a context\n manager for the ``with`` statement. If your code is intended to\n work with any file-like object, you can use the function\n ``contextlib.closing()`` instead of using the object directly.\n\nfile.flush()\n\n Flush the internal buffer, like ``stdio``\'s ``fflush()``. This may\n be a no-op on some file-like objects.\n\n Note: ``flush()`` does not necessarily write the file\'s data to disk.\n Use ``flush()`` followed by ``os.fsync()`` to ensure this\n behavior.\n\nfile.fileno()\n\n Return the integer "file descriptor" that is used by the underlying\n implementation to request I/O operations from the operating system.\n This can be useful for other, lower level interfaces that use file\n descriptors, such as the ``fcntl`` module or ``os.read()`` and\n friends.\n\n Note: File-like objects which do not have a real file descriptor should\n *not* provide this method!\n\nfile.isatty()\n\n Return ``True`` if the file is connected to a tty(-like) device,\n else ``False``.\n\n Note: If a file-like object is not associated with a real file, this\n method should *not* be implemented.\n\nfile.next()\n\n A file object is its own iterator, for example ``iter(f)`` returns\n *f* (unless *f* is closed). When a file is used as an iterator,\n typically in a ``for`` loop (for example, ``for line in f: print\n line``), the ``next()`` method is called repeatedly. This method\n returns the next input line, or raises ``StopIteration`` when EOF\n is hit when the file is open for reading (behavior is undefined\n when the file is open for writing). In order to make a ``for``\n loop the most efficient way of looping over the lines of a file (a\n very common operation), the ``next()`` method uses a hidden read-\n ahead buffer. As a consequence of using a read-ahead buffer,\n combining ``next()`` with other file methods (like ``readline()``)\n does not work right. However, using ``seek()`` to reposition the\n file to an absolute position will flush the read-ahead buffer.\n\n New in version 2.3.\n\nfile.read([size])\n\n Read at most *size* bytes from the file (less if the read hits EOF\n before obtaining *size* bytes). If the *size* argument is negative\n or omitted, read all data until EOF is reached. The bytes are\n returned as a string object. An empty string is returned when EOF\n is encountered immediately. (For certain files, like ttys, it\n makes sense to continue reading after an EOF is hit.) Note that\n this method may call the underlying C function ``fread()`` more\n than once in an effort to acquire as close to *size* bytes as\n possible. Also note that when in non-blocking mode, less data than\n was requested may be returned, even if no *size* parameter was\n given.\n\n Note: This function is simply a wrapper for the underlying ``fread()``\n C function, and will behave the same in corner cases, such as\n whether the EOF value is cached.\n\nfile.readline([size])\n\n Read one entire line from the file. A trailing newline character\n is kept in the string (but may be absent when a file ends with an\n incomplete line). [6] If the *size* argument is present and non-\n negative, it is a maximum byte count (including the trailing\n newline) and an incomplete line may be returned. An empty string is\n returned *only* when EOF is encountered immediately.\n\n Note: Unlike ``stdio``\'s ``fgets()``, the returned string contains null\n characters (``\'\\0\'``) if they occurred in the input.\n\nfile.readlines([sizehint])\n\n Read until EOF using ``readline()`` and return a list containing\n the lines thus read. If the optional *sizehint* argument is\n present, instead of reading up to EOF, whole lines totalling\n approximately *sizehint* bytes (possibly after rounding up to an\n internal buffer size) are read. Objects implementing a file-like\n interface may choose to ignore *sizehint* if it cannot be\n implemented, or cannot be implemented efficiently.\n\nfile.xreadlines()\n\n This method returns the same thing as ``iter(f)``.\n\n New in version 2.1.\n\n Deprecated since version 2.3: Use ``for line in file`` instead.\n\nfile.seek(offset[, whence])\n\n Set the file\'s current position, like ``stdio``\'s ``fseek()``. The\n *whence* argument is optional and defaults to ``os.SEEK_SET`` or\n ``0`` (absolute file positioning); other values are ``os.SEEK_CUR``\n or ``1`` (seek relative to the current position) and\n ``os.SEEK_END`` or ``2`` (seek relative to the file\'s end). There\n is no return value.\n\n For example, ``f.seek(2, os.SEEK_CUR)`` advances the position by\n two and ``f.seek(-3, os.SEEK_END)`` sets the position to the third\n to last.\n\n Note that if the file is opened for appending (mode ``\'a\'`` or\n ``\'a+\'``), any ``seek()`` operations will be undone at the next\n write. If the file is only opened for writing in append mode (mode\n ``\'a\'``), this method is essentially a no-op, but it remains useful\n for files opened in append mode with reading enabled (mode\n ``\'a+\'``). If the file is opened in text mode (without ``\'b\'``),\n only offsets returned by ``tell()`` are legal. Use of other\n offsets causes undefined behavior.\n\n Note that not all file objects are seekable.\n\n Changed in version 2.6: Passing float values as offset has been\n deprecated.\n\nfile.tell()\n\n Return the file\'s current position, like ``stdio``\'s ``ftell()``.\n\n Note: On Windows, ``tell()`` can return illegal values (after an\n ``fgets()``) when reading files with Unix-style line-endings. Use\n binary mode (``\'rb\'``) to circumvent this problem.\n\nfile.truncate([size])\n\n Truncate the file\'s size. If the optional *size* argument is\n present, the file is truncated to (at most) that size. The size\n defaults to the current position. The current file position is not\n changed. Note that if a specified size exceeds the file\'s current\n size, the result is platform-dependent: possibilities include that\n the file may remain unchanged, increase to the specified size as if\n zero-filled, or increase to the specified size with undefined new\n content. Availability: Windows, many Unix variants.\n\nfile.write(str)\n\n Write a string to the file. There is no return value. Due to\n buffering, the string may not actually show up in the file until\n the ``flush()`` or ``close()`` method is called.\n\nfile.writelines(sequence)\n\n Write a sequence of strings to the file. The sequence can be any\n iterable object producing strings, typically a list of strings.\n There is no return value. (The name is intended to match\n ``readlines()``; ``writelines()`` does not add line separators.)\n\nFiles support the iterator protocol. Each iteration returns the same\nresult as ``file.readline()``, and iteration ends when the\n``readline()`` method returns an empty string.\n\nFile objects also offer a number of other interesting attributes.\nThese are not required for file-like objects, but should be\nimplemented if they make sense for the particular object.\n\nfile.closed\n\n bool indicating the current state of the file object. This is a\n read-only attribute; the ``close()`` method changes the value. It\n may not be available on all file-like objects.\n\nfile.encoding\n\n The encoding that this file uses. When Unicode strings are written\n to a file, they will be converted to byte strings using this\n encoding. In addition, when the file is connected to a terminal,\n the attribute gives the encoding that the terminal is likely to use\n (that information might be incorrect if the user has misconfigured\n the terminal). The attribute is read-only and may not be present\n on all file-like objects. It may also be ``None``, in which case\n the file uses the system default encoding for converting Unicode\n strings.\n\n New in version 2.3.\n\nfile.errors\n\n The Unicode error handler used along with the encoding.\n\n New in version 2.6.\n\nfile.mode\n\n The I/O mode for the file. If the file was created using the\n ``open()`` built-in function, this will be the value of the *mode*\n parameter. This is a read-only attribute and may not be present on\n all file-like objects.\n\nfile.name\n\n If the file object was created using ``open()``, the name of the\n file. Otherwise, some string that indicates the source of the file\n object, of the form ``<...>``. This is a read-only attribute and\n may not be present on all file-like objects.\n\nfile.newlines\n\n If Python was built with the *--with-universal-newlines* option to\n **configure** (the default) this read-only attribute exists, and\n for files opened in universal newline read mode it keeps track of\n the types of newlines encountered while reading the file. The\n values it can take are ``\'\\r\'``, ``\'\\n\'``, ``\'\\r\\n\'``, ``None``\n (unknown, no newlines read yet) or a tuple containing all the\n newline types seen, to indicate that multiple newline conventions\n were encountered. For files not opened in universal newline read\n mode the value of this attribute will be ``None``.\n\nfile.softspace\n\n Boolean that indicates whether a space character needs to be\n printed before another value when using the ``print`` statement.\n Classes that are trying to simulate a file object should also have\n a writable ``softspace`` attribute, which should be initialized to\n zero. This will be automatic for most classes implemented in\n Python (care may be needed for objects that override attribute\n access); types implemented in C will have to provide a writable\n ``softspace`` attribute.\n\n Note: This attribute is not used to control the ``print`` statement,\n but to allow the implementation of ``print`` to keep track of its\n internal state.\n', 'bltin-null-object': u"\nThe Null Object\n***************\n\nThis object is returned by functions that don't explicitly return a\nvalue. It supports no special operations. There is exactly one null\nobject, named ``None`` (a built-in name).\n\nIt is written as ``None``.\n", 'bltin-type-objects': u"\nType Objects\n************\n\nType objects represent the various object types. An object's type is\naccessed by the built-in function ``type()``. There are no special\noperations on types. The standard module ``types`` defines names for\nall standard built-in types.\n\nTypes are written like this: ````.\n", - 'booleans': u'\nBoolean operations\n******************\n\nBoolean operations have the lowest priority of all Python operations:\n\n expression ::= conditional_expression | lambda_form\n old_expression ::= or_test | old_lambda_form\n conditional_expression ::= or_test ["if" or_test "else" expression]\n or_test ::= and_test | or_test "or" and_test\n and_test ::= not_test | and_test "and" not_test\n not_test ::= comparison | "not" not_test\n\nIn the context of Boolean operations, and also when expressions are\nused by control flow statements, the following values are interpreted\nas false: ``False``, ``None``, numeric zero of all types, and empty\nstrings and containers (including strings, tuples, lists,\ndictionaries, sets and frozensets). All other values are interpreted\nas true. (See the ``__nonzero__()`` special method for a way to\nchange this.)\n\nThe operator ``not`` yields ``True`` if its argument is false,\n``False`` otherwise.\n\nThe expression ``x if C else y`` first evaluates *C* (*not* *x*); if\n*C* is true, *x* is evaluated and its value is returned; otherwise,\n*y* is evaluated and its value is returned.\n\nNew in version 2.5.\n\nThe expression ``x and y`` first evaluates *x*; if *x* is false, its\nvalue is returned; otherwise, *y* is evaluated and the resulting value\nis returned.\n\nThe expression ``x or y`` first evaluates *x*; if *x* is true, its\nvalue is returned; otherwise, *y* is evaluated and the resulting value\nis returned.\n\n(Note that neither ``and`` nor ``or`` restrict the value and type they\nreturn to ``False`` and ``True``, but rather return the last evaluated\nargument. This is sometimes useful, e.g., if ``s`` is a string that\nshould be replaced by a default value if it is empty, the expression\n``s or \'foo\'`` yields the desired value. Because ``not`` has to\ninvent a value anyway, it does not bother to return a value of the\nsame type as its argument, so e.g., ``not \'foo\'`` yields ``False``,\nnot ``\'\'``.)\n', + 'booleans': u'\nBoolean operations\n******************\n\n or_test ::= and_test | or_test "or" and_test\n and_test ::= not_test | and_test "and" not_test\n not_test ::= comparison | "not" not_test\n\nIn the context of Boolean operations, and also when expressions are\nused by control flow statements, the following values are interpreted\nas false: ``False``, ``None``, numeric zero of all types, and empty\nstrings and containers (including strings, tuples, lists,\ndictionaries, sets and frozensets). All other values are interpreted\nas true. (See the ``__nonzero__()`` special method for a way to\nchange this.)\n\nThe operator ``not`` yields ``True`` if its argument is false,\n``False`` otherwise.\n\nThe expression ``x and y`` first evaluates *x*; if *x* is false, its\nvalue is returned; otherwise, *y* is evaluated and the resulting value\nis returned.\n\nThe expression ``x or y`` first evaluates *x*; if *x* is true, its\nvalue is returned; otherwise, *y* is evaluated and the resulting value\nis returned.\n\n(Note that neither ``and`` nor ``or`` restrict the value and type they\nreturn to ``False`` and ``True``, but rather return the last evaluated\nargument. This is sometimes useful, e.g., if ``s`` is a string that\nshould be replaced by a default value if it is empty, the expression\n``s or \'foo\'`` yields the desired value. Because ``not`` has to\ninvent a value anyway, it does not bother to return a value of the\nsame type as its argument, so e.g., ``not \'foo\'`` yields ``False``,\nnot ``\'\'``.)\n', 'break': u'\nThe ``break`` statement\n***********************\n\n break_stmt ::= "break"\n\n``break`` may only occur syntactically nested in a ``for`` or\n``while`` loop, but not nested in a function or class definition\nwithin that loop.\n\nIt terminates the nearest enclosing loop, skipping the optional\n``else`` clause if the loop has one.\n\nIf a ``for`` loop is terminated by ``break``, the loop control target\nkeeps its current value.\n\nWhen ``break`` passes control out of a ``try`` statement with a\n``finally`` clause, that ``finally`` clause is executed before really\nleaving the loop.\n', 'callable-types': u'\nEmulating callable objects\n**************************\n\nobject.__call__(self[, args...])\n\n Called when the instance is "called" as a function; if this method\n is defined, ``x(arg1, arg2, ...)`` is a shorthand for\n ``x.__call__(arg1, arg2, ...)``.\n', 'calls': u'\nCalls\n*****\n\nA call calls a callable object (e.g., a function) with a possibly\nempty series of arguments:\n\n call ::= primary "(" [argument_list [","]\n | expression genexpr_for] ")"\n argument_list ::= positional_arguments ["," keyword_arguments]\n ["," "*" expression] ["," keyword_arguments]\n ["," "**" expression]\n | keyword_arguments ["," "*" expression]\n ["," "**" expression]\n | "*" expression ["," "*" expression] ["," "**" expression]\n | "**" expression\n positional_arguments ::= expression ("," expression)*\n keyword_arguments ::= keyword_item ("," keyword_item)*\n keyword_item ::= identifier "=" expression\n\nA trailing comma may be present after the positional and keyword\narguments but does not affect the semantics.\n\nThe primary must evaluate to a callable object (user-defined\nfunctions, built-in functions, methods of built-in objects, class\nobjects, methods of class instances, and certain class instances\nthemselves are callable; extensions may define additional callable\nobject types). All argument expressions are evaluated before the call\nis attempted. Please refer to section *Function definitions* for the\nsyntax of formal parameter lists.\n\nIf keyword arguments are present, they are first converted to\npositional arguments, as follows. First, a list of unfilled slots is\ncreated for the formal parameters. If there are N positional\narguments, they are placed in the first N slots. Next, for each\nkeyword argument, the identifier is used to determine the\ncorresponding slot (if the identifier is the same as the first formal\nparameter name, the first slot is used, and so on). If the slot is\nalready filled, a ``TypeError`` exception is raised. Otherwise, the\nvalue of the argument is placed in the slot, filling it (even if the\nexpression is ``None``, it fills the slot). When all arguments have\nbeen processed, the slots that are still unfilled are filled with the\ncorresponding default value from the function definition. (Default\nvalues are calculated, once, when the function is defined; thus, a\nmutable object such as a list or dictionary used as default value will\nbe shared by all calls that don\'t specify an argument value for the\ncorresponding slot; this should usually be avoided.) If there are any\nunfilled slots for which no default value is specified, a\n``TypeError`` exception is raised. Otherwise, the list of filled\nslots is used as the argument list for the call.\n\n**CPython implementation detail:** An implementation may provide\nbuilt-in functions whose positional parameters do not have names, even\nif they are \'named\' for the purpose of documentation, and which\ntherefore cannot be supplied by keyword. In CPython, this is the case\nfor functions implemented in C that use ``PyArg_ParseTuple()`` to\nparse their arguments.\n\nIf there are more positional arguments than there are formal parameter\nslots, a ``TypeError`` exception is raised, unless a formal parameter\nusing the syntax ``*identifier`` is present; in this case, that formal\nparameter receives a tuple containing the excess positional arguments\n(or an empty tuple if there were no excess positional arguments).\n\nIf any keyword argument does not correspond to a formal parameter\nname, a ``TypeError`` exception is raised, unless a formal parameter\nusing the syntax ``**identifier`` is present; in this case, that\nformal parameter receives a dictionary containing the excess keyword\narguments (using the keywords as keys and the argument values as\ncorresponding values), or a (new) empty dictionary if there were no\nexcess keyword arguments.\n\nIf the syntax ``*expression`` appears in the function call,\n``expression`` must evaluate to a sequence. Elements from this\nsequence are treated as if they were additional positional arguments;\nif there are positional arguments *x1*,..., *xN*, and ``expression``\nevaluates to a sequence *y1*, ..., *yM*, this is equivalent to a call\nwith M+N positional arguments *x1*, ..., *xN*, *y1*, ..., *yM*.\n\nA consequence of this is that although the ``*expression`` syntax may\nappear *after* some keyword arguments, it is processed *before* the\nkeyword arguments (and the ``**expression`` argument, if any -- see\nbelow). So:\n\n >>> def f(a, b):\n ... print a, b\n ...\n >>> f(b=1, *(2,))\n 2 1\n >>> f(a=1, *(2,))\n Traceback (most recent call last):\n File "", line 1, in ?\n TypeError: f() got multiple values for keyword argument \'a\'\n >>> f(1, *(2,))\n 1 2\n\nIt is unusual for both keyword arguments and the ``*expression``\nsyntax to be used in the same call, so in practice this confusion does\nnot arise.\n\nIf the syntax ``**expression`` appears in the function call,\n``expression`` must evaluate to a mapping, the contents of which are\ntreated as additional keyword arguments. In the case of a keyword\nappearing in both ``expression`` and as an explicit keyword argument,\na ``TypeError`` exception is raised.\n\nFormal parameters using the syntax ``*identifier`` or ``**identifier``\ncannot be used as positional argument slots or as keyword argument\nnames. Formal parameters using the syntax ``(sublist)`` cannot be\nused as keyword argument names; the outermost sublist corresponds to a\nsingle unnamed argument slot, and the argument value is assigned to\nthe sublist using the usual tuple assignment rules after all other\nparameter processing is done.\n\nA call always returns some value, possibly ``None``, unless it raises\nan exception. How this value is computed depends on the type of the\ncallable object.\n\nIf it is---\n\na user-defined function:\n The code block for the function is executed, passing it the\n argument list. The first thing the code block will do is bind the\n formal parameters to the arguments; this is described in section\n *Function definitions*. When the code block executes a ``return``\n statement, this specifies the return value of the function call.\n\na built-in function or method:\n The result is up to the interpreter; see *Built-in Functions* for\n the descriptions of built-in functions and methods.\n\na class object:\n A new instance of that class is returned.\n\na class instance method:\n The corresponding user-defined function is called, with an argument\n list that is one longer than the argument list of the call: the\n instance becomes the first argument.\n\na class instance:\n The class must define a ``__call__()`` method; the effect is then\n the same as if that method was called.\n', @@ -25,34 +25,34 @@ 'continue': u'\nThe ``continue`` statement\n**************************\n\n continue_stmt ::= "continue"\n\n``continue`` may only occur syntactically nested in a ``for`` or\n``while`` loop, but not nested in a function or class definition or\n``finally`` clause within that loop. It continues with the next cycle\nof the nearest enclosing loop.\n\nWhen ``continue`` passes control out of a ``try`` statement with a\n``finally`` clause, that ``finally`` clause is executed before really\nstarting the next loop cycle.\n', 'conversions': u'\nArithmetic conversions\n**********************\n\nWhen a description of an arithmetic operator below uses the phrase\n"the numeric arguments are converted to a common type," the arguments\nare coerced using the coercion rules listed at *Coercion rules*. If\nboth arguments are standard numeric types, the following coercions are\napplied:\n\n* If either argument is a complex number, the other is converted to\n complex;\n\n* otherwise, if either argument is a floating point number, the other\n is converted to floating point;\n\n* otherwise, if either argument is a long integer, the other is\n converted to long integer;\n\n* otherwise, both must be plain integers and no conversion is\n necessary.\n\nSome additional rules apply for certain operators (e.g., a string left\nargument to the \'%\' operator). Extensions can define their own\ncoercions.\n', 'customization': u'\nBasic customization\n*******************\n\nobject.__new__(cls[, ...])\n\n Called to create a new instance of class *cls*. ``__new__()`` is a\n static method (special-cased so you need not declare it as such)\n that takes the class of which an instance was requested as its\n first argument. The remaining arguments are those passed to the\n object constructor expression (the call to the class). The return\n value of ``__new__()`` should be the new object instance (usually\n an instance of *cls*).\n\n Typical implementations create a new instance of the class by\n invoking the superclass\'s ``__new__()`` method using\n ``super(currentclass, cls).__new__(cls[, ...])`` with appropriate\n arguments and then modifying the newly-created instance as\n necessary before returning it.\n\n If ``__new__()`` returns an instance of *cls*, then the new\n instance\'s ``__init__()`` method will be invoked like\n ``__init__(self[, ...])``, where *self* is the new instance and the\n remaining arguments are the same as were passed to ``__new__()``.\n\n If ``__new__()`` does not return an instance of *cls*, then the new\n instance\'s ``__init__()`` method will not be invoked.\n\n ``__new__()`` is intended mainly to allow subclasses of immutable\n types (like int, str, or tuple) to customize instance creation. It\n is also commonly overridden in custom metaclasses in order to\n customize class creation.\n\nobject.__init__(self[, ...])\n\n Called when the instance is created. The arguments are those\n passed to the class constructor expression. If a base class has an\n ``__init__()`` method, the derived class\'s ``__init__()`` method,\n if any, must explicitly call it to ensure proper initialization of\n the base class part of the instance; for example:\n ``BaseClass.__init__(self, [args...])``. As a special constraint\n on constructors, no value may be returned; doing so will cause a\n ``TypeError`` to be raised at runtime.\n\nobject.__del__(self)\n\n Called when the instance is about to be destroyed. This is also\n called a destructor. If a base class has a ``__del__()`` method,\n the derived class\'s ``__del__()`` method, if any, must explicitly\n call it to ensure proper deletion of the base class part of the\n instance. Note that it is possible (though not recommended!) for\n the ``__del__()`` method to postpone destruction of the instance by\n creating a new reference to it. It may then be called at a later\n time when this new reference is deleted. It is not guaranteed that\n ``__del__()`` methods are called for objects that still exist when\n the interpreter exits.\n\n Note: ``del x`` doesn\'t directly call ``x.__del__()`` --- the former\n decrements the reference count for ``x`` by one, and the latter\n is only called when ``x``\'s reference count reaches zero. Some\n common situations that may prevent the reference count of an\n object from going to zero include: circular references between\n objects (e.g., a doubly-linked list or a tree data structure with\n parent and child pointers); a reference to the object on the\n stack frame of a function that caught an exception (the traceback\n stored in ``sys.exc_traceback`` keeps the stack frame alive); or\n a reference to the object on the stack frame that raised an\n unhandled exception in interactive mode (the traceback stored in\n ``sys.last_traceback`` keeps the stack frame alive). The first\n situation can only be remedied by explicitly breaking the cycles;\n the latter two situations can be resolved by storing ``None`` in\n ``sys.exc_traceback`` or ``sys.last_traceback``. Circular\n references which are garbage are detected when the option cycle\n detector is enabled (it\'s on by default), but can only be cleaned\n up if there are no Python-level ``__del__()`` methods involved.\n Refer to the documentation for the ``gc`` module for more\n information about how ``__del__()`` methods are handled by the\n cycle detector, particularly the description of the ``garbage``\n value.\n\n Warning: Due to the precarious circumstances under which ``__del__()``\n methods are invoked, exceptions that occur during their execution\n are ignored, and a warning is printed to ``sys.stderr`` instead.\n Also, when ``__del__()`` is invoked in response to a module being\n deleted (e.g., when execution of the program is done), other\n globals referenced by the ``__del__()`` method may already have\n been deleted or in the process of being torn down (e.g. the\n import machinery shutting down). For this reason, ``__del__()``\n methods should do the absolute minimum needed to maintain\n external invariants. Starting with version 1.5, Python\n guarantees that globals whose name begins with a single\n underscore are deleted from their module before other globals are\n deleted; if no other references to such globals exist, this may\n help in assuring that imported modules are still available at the\n time when the ``__del__()`` method is called.\n\nobject.__repr__(self)\n\n Called by the ``repr()`` built-in function and by string\n conversions (reverse quotes) to compute the "official" string\n representation of an object. If at all possible, this should look\n like a valid Python expression that could be used to recreate an\n object with the same value (given an appropriate environment). If\n this is not possible, a string of the form ``<...some useful\n description...>`` should be returned. The return value must be a\n string object. If a class defines ``__repr__()`` but not\n ``__str__()``, then ``__repr__()`` is also used when an "informal"\n string representation of instances of that class is required.\n\n This is typically used for debugging, so it is important that the\n representation is information-rich and unambiguous.\n\nobject.__str__(self)\n\n Called by the ``str()`` built-in function and by the ``print``\n statement to compute the "informal" string representation of an\n object. This differs from ``__repr__()`` in that it does not have\n to be a valid Python expression: a more convenient or concise\n representation may be used instead. The return value must be a\n string object.\n\nobject.__lt__(self, other)\nobject.__le__(self, other)\nobject.__eq__(self, other)\nobject.__ne__(self, other)\nobject.__gt__(self, other)\nobject.__ge__(self, other)\n\n New in version 2.1.\n\n These are the so-called "rich comparison" methods, and are called\n for comparison operators in preference to ``__cmp__()`` below. The\n correspondence between operator symbols and method names is as\n follows: ``xy`` call ``x.__ne__(y)``, ``x>y`` calls ``x.__gt__(y)``, and\n ``x>=y`` calls ``x.__ge__(y)``.\n\n A rich comparison method may return the singleton\n ``NotImplemented`` if it does not implement the operation for a\n given pair of arguments. By convention, ``False`` and ``True`` are\n returned for a successful comparison. However, these methods can\n return any value, so if the comparison operator is used in a\n Boolean context (e.g., in the condition of an ``if`` statement),\n Python will call ``bool()`` on the value to determine if the result\n is true or false.\n\n There are no implied relationships among the comparison operators.\n The truth of ``x==y`` does not imply that ``x!=y`` is false.\n Accordingly, when defining ``__eq__()``, one should also define\n ``__ne__()`` so that the operators will behave as expected. See\n the paragraph on ``__hash__()`` for some important notes on\n creating *hashable* objects which support custom comparison\n operations and are usable as dictionary keys.\n\n There are no swapped-argument versions of these methods (to be used\n when the left argument does not support the operation but the right\n argument does); rather, ``__lt__()`` and ``__gt__()`` are each\n other\'s reflection, ``__le__()`` and ``__ge__()`` are each other\'s\n reflection, and ``__eq__()`` and ``__ne__()`` are their own\n reflection.\n\n Arguments to rich comparison methods are never coerced.\n\n To automatically generate ordering operations from a single root\n operation, see the Total Ordering recipe in the ASPN cookbook.\n\nobject.__cmp__(self, other)\n\n Called by comparison operations if rich comparison (see above) is\n not defined. Should return a negative integer if ``self < other``,\n zero if ``self == other``, a positive integer if ``self > other``.\n If no ``__cmp__()``, ``__eq__()`` or ``__ne__()`` operation is\n defined, class instances are compared by object identity\n ("address"). See also the description of ``__hash__()`` for some\n important notes on creating *hashable* objects which support custom\n comparison operations and are usable as dictionary keys. (Note: the\n restriction that exceptions are not propagated by ``__cmp__()`` has\n been removed since Python 1.5.)\n\nobject.__rcmp__(self, other)\n\n Changed in version 2.1: No longer supported.\n\nobject.__hash__(self)\n\n Called by built-in function ``hash()`` and for operations on\n members of hashed collections including ``set``, ``frozenset``, and\n ``dict``. ``__hash__()`` should return an integer. The only\n required property is that objects which compare equal have the same\n hash value; it is advised to somehow mix together (e.g. using\n exclusive or) the hash values for the components of the object that\n also play a part in comparison of objects.\n\n If a class does not define a ``__cmp__()`` or ``__eq__()`` method\n it should not define a ``__hash__()`` operation either; if it\n defines ``__cmp__()`` or ``__eq__()`` but not ``__hash__()``, its\n instances will not be usable in hashed collections. If a class\n defines mutable objects and implements a ``__cmp__()`` or\n ``__eq__()`` method, it should not implement ``__hash__()``, since\n hashable collection implementations require that a object\'s hash\n value is immutable (if the object\'s hash value changes, it will be\n in the wrong hash bucket).\n\n User-defined classes have ``__cmp__()`` and ``__hash__()`` methods\n by default; with them, all objects compare unequal (except with\n themselves) and ``x.__hash__()`` returns ``id(x)``.\n\n Classes which inherit a ``__hash__()`` method from a parent class\n but change the meaning of ``__cmp__()`` or ``__eq__()`` such that\n the hash value returned is no longer appropriate (e.g. by switching\n to a value-based concept of equality instead of the default\n identity based equality) can explicitly flag themselves as being\n unhashable by setting ``__hash__ = None`` in the class definition.\n Doing so means that not only will instances of the class raise an\n appropriate ``TypeError`` when a program attempts to retrieve their\n hash value, but they will also be correctly identified as\n unhashable when checking ``isinstance(obj, collections.Hashable)``\n (unlike classes which define their own ``__hash__()`` to explicitly\n raise ``TypeError``).\n\n Changed in version 2.5: ``__hash__()`` may now also return a long\n integer object; the 32-bit integer is then derived from the hash of\n that object.\n\n Changed in version 2.6: ``__hash__`` may now be set to ``None`` to\n explicitly flag instances of a class as unhashable.\n\nobject.__nonzero__(self)\n\n Called to implement truth value testing and the built-in operation\n ``bool()``; should return ``False`` or ``True``, or their integer\n equivalents ``0`` or ``1``. When this method is not defined,\n ``__len__()`` is called, if it is defined, and the object is\n considered true if its result is nonzero. If a class defines\n neither ``__len__()`` nor ``__nonzero__()``, all its instances are\n considered true.\n\nobject.__unicode__(self)\n\n Called to implement ``unicode()`` built-in; should return a Unicode\n object. When this method is not defined, string conversion is\n attempted, and the result of string conversion is converted to\n Unicode using the system default encoding.\n', - 'debugger': u'\n``pdb`` --- The Python Debugger\n*******************************\n\nThe module ``pdb`` defines an interactive source code debugger for\nPython programs. It supports setting (conditional) breakpoints and\nsingle stepping at the source line level, inspection of stack frames,\nsource code listing, and evaluation of arbitrary Python code in the\ncontext of any stack frame. It also supports post-mortem debugging\nand can be called under program control.\n\nThe debugger is extensible --- it is actually defined as the class\n``Pdb``. This is currently undocumented but easily understood by\nreading the source. The extension interface uses the modules ``bdb``\n(undocumented) and ``cmd``.\n\nThe debugger\'s prompt is ``(Pdb)``. Typical usage to run a program\nunder control of the debugger is:\n\n >>> import pdb\n >>> import mymodule\n >>> pdb.run(\'mymodule.test()\')\n > (0)?()\n (Pdb) continue\n > (1)?()\n (Pdb) continue\n NameError: \'spam\'\n > (1)?()\n (Pdb)\n\n``pdb.py`` can also be invoked as a script to debug other scripts.\nFor example:\n\n python -m pdb myscript.py\n\nWhen invoked as a script, pdb will automatically enter post-mortem\ndebugging if the program being debugged exits abnormally. After post-\nmortem debugging (or after normal exit of the program), pdb will\nrestart the program. Automatic restarting preserves pdb\'s state (such\nas breakpoints) and in most cases is more useful than quitting the\ndebugger upon program\'s exit.\n\nNew in version 2.4: Restarting post-mortem behavior added.\n\nThe typical usage to break into the debugger from a running program is\nto insert\n\n import pdb; pdb.set_trace()\n\nat the location you want to break into the debugger. You can then\nstep through the code following this statement, and continue running\nwithout the debugger using the ``c`` command.\n\nThe typical usage to inspect a crashed program is:\n\n >>> import pdb\n >>> import mymodule\n >>> mymodule.test()\n Traceback (most recent call last):\n File "", line 1, in ?\n File "./mymodule.py", line 4, in test\n test2()\n File "./mymodule.py", line 3, in test2\n print spam\n NameError: spam\n >>> pdb.pm()\n > ./mymodule.py(3)test2()\n -> print spam\n (Pdb)\n\nThe module defines the following functions; each enters the debugger\nin a slightly different way:\n\npdb.run(statement[, globals[, locals]])\n\n Execute the *statement* (given as a string) under debugger control.\n The debugger prompt appears before any code is executed; you can\n set breakpoints and type ``continue``, or you can step through the\n statement using ``step`` or ``next`` (all these commands are\n explained below). The optional *globals* and *locals* arguments\n specify the environment in which the code is executed; by default\n the dictionary of the module ``__main__`` is used. (See the\n explanation of the ``exec`` statement or the ``eval()`` built-in\n function.)\n\npdb.runeval(expression[, globals[, locals]])\n\n Evaluate the *expression* (given as a string) under debugger\n control. When ``runeval()`` returns, it returns the value of the\n expression. Otherwise this function is similar to ``run()``.\n\npdb.runcall(function[, argument, ...])\n\n Call the *function* (a function or method object, not a string)\n with the given arguments. When ``runcall()`` returns, it returns\n whatever the function call returned. The debugger prompt appears\n as soon as the function is entered.\n\npdb.set_trace()\n\n Enter the debugger at the calling stack frame. This is useful to\n hard-code a breakpoint at a given point in a program, even if the\n code is not otherwise being debugged (e.g. when an assertion\n fails).\n\npdb.post_mortem([traceback])\n\n Enter post-mortem debugging of the given *traceback* object. If no\n *traceback* is given, it uses the one of the exception that is\n currently being handled (an exception must be being handled if the\n default is to be used).\n\npdb.pm()\n\n Enter post-mortem debugging of the traceback found in\n ``sys.last_traceback``.\n', + 'debugger': u'\n``pdb`` --- The Python Debugger\n*******************************\n\nThe module ``pdb`` defines an interactive source code debugger for\nPython programs. It supports setting (conditional) breakpoints and\nsingle stepping at the source line level, inspection of stack frames,\nsource code listing, and evaluation of arbitrary Python code in the\ncontext of any stack frame. It also supports post-mortem debugging\nand can be called under program control.\n\nThe debugger is extensible --- it is actually defined as the class\n``Pdb``. This is currently undocumented but easily understood by\nreading the source. The extension interface uses the modules ``bdb``\nand ``cmd``.\n\nThe debugger\'s prompt is ``(Pdb)``. Typical usage to run a program\nunder control of the debugger is:\n\n >>> import pdb\n >>> import mymodule\n >>> pdb.run(\'mymodule.test()\')\n > (0)?()\n (Pdb) continue\n > (1)?()\n (Pdb) continue\n NameError: \'spam\'\n > (1)?()\n (Pdb)\n\n``pdb.py`` can also be invoked as a script to debug other scripts.\nFor example:\n\n python -m pdb myscript.py\n\nWhen invoked as a script, pdb will automatically enter post-mortem\ndebugging if the program being debugged exits abnormally. After post-\nmortem debugging (or after normal exit of the program), pdb will\nrestart the program. Automatic restarting preserves pdb\'s state (such\nas breakpoints) and in most cases is more useful than quitting the\ndebugger upon program\'s exit.\n\nNew in version 2.4: Restarting post-mortem behavior added.\n\nThe typical usage to break into the debugger from a running program is\nto insert\n\n import pdb; pdb.set_trace()\n\nat the location you want to break into the debugger. You can then\nstep through the code following this statement, and continue running\nwithout the debugger using the ``c`` command.\n\nThe typical usage to inspect a crashed program is:\n\n >>> import pdb\n >>> import mymodule\n >>> mymodule.test()\n Traceback (most recent call last):\n File "", line 1, in ?\n File "./mymodule.py", line 4, in test\n test2()\n File "./mymodule.py", line 3, in test2\n print spam\n NameError: spam\n >>> pdb.pm()\n > ./mymodule.py(3)test2()\n -> print spam\n (Pdb)\n\nThe module defines the following functions; each enters the debugger\nin a slightly different way:\n\npdb.run(statement[, globals[, locals]])\n\n Execute the *statement* (given as a string) under debugger control.\n The debugger prompt appears before any code is executed; you can\n set breakpoints and type ``continue``, or you can step through the\n statement using ``step`` or ``next`` (all these commands are\n explained below). The optional *globals* and *locals* arguments\n specify the environment in which the code is executed; by default\n the dictionary of the module ``__main__`` is used. (See the\n explanation of the ``exec`` statement or the ``eval()`` built-in\n function.)\n\npdb.runeval(expression[, globals[, locals]])\n\n Evaluate the *expression* (given as a string) under debugger\n control. When ``runeval()`` returns, it returns the value of the\n expression. Otherwise this function is similar to ``run()``.\n\npdb.runcall(function[, argument, ...])\n\n Call the *function* (a function or method object, not a string)\n with the given arguments. When ``runcall()`` returns, it returns\n whatever the function call returned. The debugger prompt appears\n as soon as the function is entered.\n\npdb.set_trace()\n\n Enter the debugger at the calling stack frame. This is useful to\n hard-code a breakpoint at a given point in a program, even if the\n code is not otherwise being debugged (e.g. when an assertion\n fails).\n\npdb.post_mortem([traceback])\n\n Enter post-mortem debugging of the given *traceback* object. If no\n *traceback* is given, it uses the one of the exception that is\n currently being handled (an exception must be being handled if the\n default is to be used).\n\npdb.pm()\n\n Enter post-mortem debugging of the traceback found in\n ``sys.last_traceback``.\n', 'del': u'\nThe ``del`` statement\n*********************\n\n del_stmt ::= "del" target_list\n\nDeletion is recursively defined very similar to the way assignment is\ndefined. Rather that spelling it out in full details, here are some\nhints.\n\nDeletion of a target list recursively deletes each target, from left\nto right.\n\nDeletion of a name removes the binding of that name from the local or\nglobal namespace, depending on whether the name occurs in a ``global``\nstatement in the same code block. If the name is unbound, a\n``NameError`` exception will be raised.\n\nIt is illegal to delete a name from the local namespace if it occurs\nas a free variable in a nested block.\n\nDeletion of attribute references, subscriptions and slicings is passed\nto the primary object involved; deletion of a slicing is in general\nequivalent to assignment of an empty slice of the right type (but even\nthis is determined by the sliced object).\n', 'dict': u'\nDictionary displays\n*******************\n\nA dictionary display is a possibly empty series of key/datum pairs\nenclosed in curly braces:\n\n dict_display ::= "{" [key_datum_list] "}"\n key_datum_list ::= key_datum ("," key_datum)* [","]\n key_datum ::= expression ":" expression\n\nA dictionary display yields a new dictionary object.\n\nThe key/datum pairs are evaluated from left to right to define the\nentries of the dictionary: each key object is used as a key into the\ndictionary to store the corresponding datum.\n\nRestrictions on the types of the key values are listed earlier in\nsection *The standard type hierarchy*. (To summarize, the key type\nshould be *hashable*, which excludes all mutable objects.) Clashes\nbetween duplicate keys are not detected; the last datum (textually\nrightmost in the display) stored for a given key value prevails.\n', 'dynamic-features': u'\nInteraction with dynamic features\n*********************************\n\nThere are several cases where Python statements are illegal when used\nin conjunction with nested scopes that contain free variables.\n\nIf a variable is referenced in an enclosing scope, it is illegal to\ndelete the name. An error will be reported at compile time.\n\nIf the wild card form of import --- ``import *`` --- is used in a\nfunction and the function contains or is a nested block with free\nvariables, the compiler will raise a ``SyntaxError``.\n\nIf ``exec`` is used in a function and the function contains or is a\nnested block with free variables, the compiler will raise a\n``SyntaxError`` unless the exec explicitly specifies the local\nnamespace for the ``exec``. (In other words, ``exec obj`` would be\nillegal, but ``exec obj in ns`` would be legal.)\n\nThe ``eval()``, ``execfile()``, and ``input()`` functions and the\n``exec`` statement do not have access to the full environment for\nresolving names. Names may be resolved in the local and global\nnamespaces of the caller. Free variables are not resolved in the\nnearest enclosing namespace, but in the global namespace. [1] The\n``exec`` statement and the ``eval()`` and ``execfile()`` functions\nhave optional arguments to override the global and local namespace.\nIf only one namespace is specified, it is used for both.\n', 'else': u'\nThe ``if`` statement\n********************\n\nThe ``if`` statement is used for conditional execution:\n\n if_stmt ::= "if" expression ":" suite\n ( "elif" expression ":" suite )*\n ["else" ":" suite]\n\nIt selects exactly one of the suites by evaluating the expressions one\nby one until one is found to be true (see section *Boolean operations*\nfor the definition of true and false); then that suite is executed\n(and no other part of the ``if`` statement is executed or evaluated).\nIf all expressions are false, the suite of the ``else`` clause, if\npresent, is executed.\n', 'exceptions': u'\nExceptions\n**********\n\nExceptions are a means of breaking out of the normal flow of control\nof a code block in order to handle errors or other exceptional\nconditions. An exception is *raised* at the point where the error is\ndetected; it may be *handled* by the surrounding code block or by any\ncode block that directly or indirectly invoked the code block where\nthe error occurred.\n\nThe Python interpreter raises an exception when it detects a run-time\nerror (such as division by zero). A Python program can also\nexplicitly raise an exception with the ``raise`` statement. Exception\nhandlers are specified with the ``try`` ... ``except`` statement. The\n``finally`` clause of such a statement can be used to specify cleanup\ncode which does not handle the exception, but is executed whether an\nexception occurred or not in the preceding code.\n\nPython uses the "termination" model of error handling: an exception\nhandler can find out what happened and continue execution at an outer\nlevel, but it cannot repair the cause of the error and retry the\nfailing operation (except by re-entering the offending piece of code\nfrom the top).\n\nWhen an exception is not handled at all, the interpreter terminates\nexecution of the program, or returns to its interactive main loop. In\neither case, it prints a stack backtrace, except when the exception is\n``SystemExit``.\n\nExceptions are identified by class instances. The ``except`` clause\nis selected depending on the class of the instance: it must reference\nthe class of the instance or a base class thereof. The instance can\nbe received by the handler and can carry additional information about\nthe exceptional condition.\n\nExceptions can also be identified by strings, in which case the\n``except`` clause is selected by object identity. An arbitrary value\ncan be raised along with the identifying string which can be passed to\nthe handler.\n\nNote: Messages to exceptions are not part of the Python API. Their\n contents may change from one version of Python to the next without\n warning and should not be relied on by code which will run under\n multiple versions of the interpreter.\n\nSee also the description of the ``try`` statement in section *The try\nstatement* and ``raise`` statement in section *The raise statement*.\n\n-[ Footnotes ]-\n\n[1] This limitation occurs because the code that is executed by these\n operations is not available at the time the module is compiled.\n', 'exec': u'\nThe ``exec`` statement\n**********************\n\n exec_stmt ::= "exec" or_expr ["in" expression ["," expression]]\n\nThis statement supports dynamic execution of Python code. The first\nexpression should evaluate to either a string, an open file object, or\na code object. If it is a string, the string is parsed as a suite of\nPython statements which is then executed (unless a syntax error\noccurs). [1] If it is an open file, the file is parsed until EOF and\nexecuted. If it is a code object, it is simply executed. In all\ncases, the code that\'s executed is expected to be valid as file input\n(see section *File input*). Be aware that the ``return`` and\n``yield`` statements may not be used outside of function definitions\neven within the context of code passed to the ``exec`` statement.\n\nIn all cases, if the optional parts are omitted, the code is executed\nin the current scope. If only the first expression after ``in`` is\nspecified, it should be a dictionary, which will be used for both the\nglobal and the local variables. If two expressions are given, they\nare used for the global and local variables, respectively. If\nprovided, *locals* can be any mapping object.\n\nChanged in version 2.4: Formerly, *locals* was required to be a\ndictionary.\n\nAs a side effect, an implementation may insert additional keys into\nthe dictionaries given besides those corresponding to variable names\nset by the executed code. For example, the current implementation may\nadd a reference to the dictionary of the built-in module\n``__builtin__`` under the key ``__builtins__`` (!).\n\n**Programmer\'s hints:** dynamic evaluation of expressions is supported\nby the built-in function ``eval()``. The built-in functions\n``globals()`` and ``locals()`` return the current global and local\ndictionary, respectively, which may be useful to pass around for use\nby ``exec``.\n\n-[ Footnotes ]-\n\n[1] Note that the parser only accepts the Unix-style end of line\n convention. If you are reading the code from a file, make sure to\n use universal newline mode to convert Windows or Mac-style\n newlines.\n', - 'execmodel': u'\nExecution model\n***************\n\n\nNaming and binding\n==================\n\n*Names* refer to objects. Names are introduced by name binding\noperations. Each occurrence of a name in the program text refers to\nthe *binding* of that name established in the innermost function block\ncontaining the use.\n\nA *block* is a piece of Python program text that is executed as a\nunit. The following are blocks: a module, a function body, and a class\ndefinition. Each command typed interactively is a block. A script\nfile (a file given as standard input to the interpreter or specified\non the interpreter command line the first argument) is a code block.\nA script command (a command specified on the interpreter command line\nwith the \'**-c**\' option) is a code block. The file read by the\nbuilt-in function ``execfile()`` is a code block. The string argument\npassed to the built-in function ``eval()`` and to the ``exec``\nstatement is a code block. The expression read and evaluated by the\nbuilt-in function ``input()`` is a code block.\n\nA code block is executed in an *execution frame*. A frame contains\nsome administrative information (used for debugging) and determines\nwhere and how execution continues after the code block\'s execution has\ncompleted.\n\nA *scope* defines the visibility of a name within a block. If a local\nvariable is defined in a block, its scope includes that block. If the\ndefinition occurs in a function block, the scope extends to any blocks\ncontained within the defining one, unless a contained block introduces\na different binding for the name. The scope of names defined in a\nclass block is limited to the class block; it does not extend to the\ncode blocks of methods -- this includes generator expressions since\nthey are implemented using a function scope. This means that the\nfollowing will fail:\n\n class A:\n a = 42\n b = list(a + i for i in range(10))\n\nWhen a name is used in a code block, it is resolved using the nearest\nenclosing scope. The set of all such scopes visible to a code block\nis called the block\'s *environment*.\n\nIf a name is bound in a block, it is a local variable of that block.\nIf a name is bound at the module level, it is a global variable. (The\nvariables of the module code block are local and global.) If a\nvariable is used in a code block but not defined there, it is a *free\nvariable*.\n\nWhen a name is not found at all, a ``NameError`` exception is raised.\nIf the name refers to a local variable that has not been bound, a\n``UnboundLocalError`` exception is raised. ``UnboundLocalError`` is a\nsubclass of ``NameError``.\n\nThe following constructs bind names: formal parameters to functions,\n``import`` statements, class and function definitions (these bind the\nclass or function name in the defining block), and targets that are\nidentifiers if occurring in an assignment, ``for`` loop header, in the\nsecond position of an ``except`` clause header or after ``as`` in a\n``with`` statement. The ``import`` statement of the form ``from ...\nimport *`` binds all names defined in the imported module, except\nthose beginning with an underscore. This form may only be used at the\nmodule level.\n\nA target occurring in a ``del`` statement is also considered bound for\nthis purpose (though the actual semantics are to unbind the name). It\nis illegal to unbind a name that is referenced by an enclosing scope;\nthe compiler will report a ``SyntaxError``.\n\nEach assignment or import statement occurs within a block defined by a\nclass or function definition or at the module level (the top-level\ncode block).\n\nIf a name binding operation occurs anywhere within a code block, all\nuses of the name within the block are treated as references to the\ncurrent block. This can lead to errors when a name is used within a\nblock before it is bound. This rule is subtle. Python lacks\ndeclarations and allows name binding operations to occur anywhere\nwithin a code block. The local variables of a code block can be\ndetermined by scanning the entire text of the block for name binding\noperations.\n\nIf the global statement occurs within a block, all uses of the name\nspecified in the statement refer to the binding of that name in the\ntop-level namespace. Names are resolved in the top-level namespace by\nsearching the global namespace, i.e. the namespace of the module\ncontaining the code block, and the builtins namespace, the namespace\nof the module ``__builtin__``. The global namespace is searched\nfirst. If the name is not found there, the builtins namespace is\nsearched. The global statement must precede all uses of the name.\n\nThe built-in namespace associated with the execution of a code block\nis actually found by looking up the name ``__builtins__`` in its\nglobal namespace; this should be a dictionary or a module (in the\nlatter case the module\'s dictionary is used). By default, when in the\n``__main__`` module, ``__builtins__`` is the built-in module\n``__builtin__`` (note: no \'s\'); when in any other module,\n``__builtins__`` is an alias for the dictionary of the ``__builtin__``\nmodule itself. ``__builtins__`` can be set to a user-created\ndictionary to create a weak form of restricted execution.\n\n**CPython implementation detail:** Users should not touch\n``__builtins__``; it is strictly an implementation detail. Users\nwanting to override values in the built-in namespace should ``import``\nthe ``__builtin__`` (no \'s\') module and modify its attributes\nappropriately.\n\nThe namespace for a module is automatically created the first time a\nmodule is imported. The main module for a script is always called\n``__main__``.\n\nThe global statement has the same scope as a name binding operation in\nthe same block. If the nearest enclosing scope for a free variable\ncontains a global statement, the free variable is treated as a global.\n\nA class definition is an executable statement that may use and define\nnames. These references follow the normal rules for name resolution.\nThe namespace of the class definition becomes the attribute dictionary\nof the class. Names defined at the class scope are not visible in\nmethods.\n\n\nInteraction with dynamic features\n---------------------------------\n\nThere are several cases where Python statements are illegal when used\nin conjunction with nested scopes that contain free variables.\n\nIf a variable is referenced in an enclosing scope, it is illegal to\ndelete the name. An error will be reported at compile time.\n\nIf the wild card form of import --- ``import *`` --- is used in a\nfunction and the function contains or is a nested block with free\nvariables, the compiler will raise a ``SyntaxError``.\n\nIf ``exec`` is used in a function and the function contains or is a\nnested block with free variables, the compiler will raise a\n``SyntaxError`` unless the exec explicitly specifies the local\nnamespace for the ``exec``. (In other words, ``exec obj`` would be\nillegal, but ``exec obj in ns`` would be legal.)\n\nThe ``eval()``, ``execfile()``, and ``input()`` functions and the\n``exec`` statement do not have access to the full environment for\nresolving names. Names may be resolved in the local and global\nnamespaces of the caller. Free variables are not resolved in the\nnearest enclosing namespace, but in the global namespace. [1] The\n``exec`` statement and the ``eval()`` and ``execfile()`` functions\nhave optional arguments to override the global and local namespace.\nIf only one namespace is specified, it is used for both.\n\n\nExceptions\n==========\n\nExceptions are a means of breaking out of the normal flow of control\nof a code block in order to handle errors or other exceptional\nconditions. An exception is *raised* at the point where the error is\ndetected; it may be *handled* by the surrounding code block or by any\ncode block that directly or indirectly invoked the code block where\nthe error occurred.\n\nThe Python interpreter raises an exception when it detects a run-time\nerror (such as division by zero). A Python program can also\nexplicitly raise an exception with the ``raise`` statement. Exception\nhandlers are specified with the ``try`` ... ``except`` statement. The\n``finally`` clause of such a statement can be used to specify cleanup\ncode which does not handle the exception, but is executed whether an\nexception occurred or not in the preceding code.\n\nPython uses the "termination" model of error handling: an exception\nhandler can find out what happened and continue execution at an outer\nlevel, but it cannot repair the cause of the error and retry the\nfailing operation (except by re-entering the offending piece of code\nfrom the top).\n\nWhen an exception is not handled at all, the interpreter terminates\nexecution of the program, or returns to its interactive main loop. In\neither case, it prints a stack backtrace, except when the exception is\n``SystemExit``.\n\nExceptions are identified by class instances. The ``except`` clause\nis selected depending on the class of the instance: it must reference\nthe class of the instance or a base class thereof. The instance can\nbe received by the handler and can carry additional information about\nthe exceptional condition.\n\nExceptions can also be identified by strings, in which case the\n``except`` clause is selected by object identity. An arbitrary value\ncan be raised along with the identifying string which can be passed to\nthe handler.\n\nNote: Messages to exceptions are not part of the Python API. Their\n contents may change from one version of Python to the next without\n warning and should not be relied on by code which will run under\n multiple versions of the interpreter.\n\nSee also the description of the ``try`` statement in section *The try\nstatement* and ``raise`` statement in section *The raise statement*.\n\n-[ Footnotes ]-\n\n[1] This limitation occurs because the code that is executed by these\n operations is not available at the time the module is compiled.\n', + 'execmodel': u'\nExecution model\n***************\n\n\nNaming and binding\n==================\n\n*Names* refer to objects. Names are introduced by name binding\noperations. Each occurrence of a name in the program text refers to\nthe *binding* of that name established in the innermost function block\ncontaining the use.\n\nA *block* is a piece of Python program text that is executed as a\nunit. The following are blocks: a module, a function body, and a class\ndefinition. Each command typed interactively is a block. A script\nfile (a file given as standard input to the interpreter or specified\non the interpreter command line the first argument) is a code block.\nA script command (a command specified on the interpreter command line\nwith the \'**-c**\' option) is a code block. The file read by the\nbuilt-in function ``execfile()`` is a code block. The string argument\npassed to the built-in function ``eval()`` and to the ``exec``\nstatement is a code block. The expression read and evaluated by the\nbuilt-in function ``input()`` is a code block.\n\nA code block is executed in an *execution frame*. A frame contains\nsome administrative information (used for debugging) and determines\nwhere and how execution continues after the code block\'s execution has\ncompleted.\n\nA *scope* defines the visibility of a name within a block. If a local\nvariable is defined in a block, its scope includes that block. If the\ndefinition occurs in a function block, the scope extends to any blocks\ncontained within the defining one, unless a contained block introduces\na different binding for the name. The scope of names defined in a\nclass block is limited to the class block; it does not extend to the\ncode blocks of methods -- this includes generator expressions since\nthey are implemented using a function scope. This means that the\nfollowing will fail:\n\n class A:\n a = 42\n b = list(a + i for i in range(10))\n\nWhen a name is used in a code block, it is resolved using the nearest\nenclosing scope. The set of all such scopes visible to a code block\nis called the block\'s *environment*.\n\nIf a name is bound in a block, it is a local variable of that block.\nIf a name is bound at the module level, it is a global variable. (The\nvariables of the module code block are local and global.) If a\nvariable is used in a code block but not defined there, it is a *free\nvariable*.\n\nWhen a name is not found at all, a ``NameError`` exception is raised.\nIf the name refers to a local variable that has not been bound, a\n``UnboundLocalError`` exception is raised. ``UnboundLocalError`` is a\nsubclass of ``NameError``.\n\nThe following constructs bind names: formal parameters to functions,\n``import`` statements, class and function definitions (these bind the\nclass or function name in the defining block), and targets that are\nidentifiers if occurring in an assignment, ``for`` loop header, in the\nsecond position of an ``except`` clause header or after ``as`` in a\n``with`` statement. The ``import`` statement of the form ``from ...\nimport *`` binds all names defined in the imported module, except\nthose beginning with an underscore. This form may only be used at the\nmodule level.\n\nA target occurring in a ``del`` statement is also considered bound for\nthis purpose (though the actual semantics are to unbind the name). It\nis illegal to unbind a name that is referenced by an enclosing scope;\nthe compiler will report a ``SyntaxError``.\n\nEach assignment or import statement occurs within a block defined by a\nclass or function definition or at the module level (the top-level\ncode block).\n\nIf a name binding operation occurs anywhere within a code block, all\nuses of the name within the block are treated as references to the\ncurrent block. This can lead to errors when a name is used within a\nblock before it is bound. This rule is subtle. Python lacks\ndeclarations and allows name binding operations to occur anywhere\nwithin a code block. The local variables of a code block can be\ndetermined by scanning the entire text of the block for name binding\noperations.\n\nIf the global statement occurs within a block, all uses of the name\nspecified in the statement refer to the binding of that name in the\ntop-level namespace. Names are resolved in the top-level namespace by\nsearching the global namespace, i.e. the namespace of the module\ncontaining the code block, and the builtins namespace, the namespace\nof the module ``__builtin__``. The global namespace is searched\nfirst. If the name is not found there, the builtins namespace is\nsearched. The global statement must precede all uses of the name.\n\nThe builtins namespace associated with the execution of a code block\nis actually found by looking up the name ``__builtins__`` in its\nglobal namespace; this should be a dictionary or a module (in the\nlatter case the module\'s dictionary is used). By default, when in the\n``__main__`` module, ``__builtins__`` is the built-in module\n``__builtin__`` (note: no \'s\'); when in any other module,\n``__builtins__`` is an alias for the dictionary of the ``__builtin__``\nmodule itself. ``__builtins__`` can be set to a user-created\ndictionary to create a weak form of restricted execution.\n\n**CPython implementation detail:** Users should not touch\n``__builtins__``; it is strictly an implementation detail. Users\nwanting to override values in the builtins namespace should ``import``\nthe ``__builtin__`` (no \'s\') module and modify its attributes\nappropriately.\n\nThe namespace for a module is automatically created the first time a\nmodule is imported. The main module for a script is always called\n``__main__``.\n\nThe global statement has the same scope as a name binding operation in\nthe same block. If the nearest enclosing scope for a free variable\ncontains a global statement, the free variable is treated as a global.\n\nA class definition is an executable statement that may use and define\nnames. These references follow the normal rules for name resolution.\nThe namespace of the class definition becomes the attribute dictionary\nof the class. Names defined at the class scope are not visible in\nmethods.\n\n\nInteraction with dynamic features\n---------------------------------\n\nThere are several cases where Python statements are illegal when used\nin conjunction with nested scopes that contain free variables.\n\nIf a variable is referenced in an enclosing scope, it is illegal to\ndelete the name. An error will be reported at compile time.\n\nIf the wild card form of import --- ``import *`` --- is used in a\nfunction and the function contains or is a nested block with free\nvariables, the compiler will raise a ``SyntaxError``.\n\nIf ``exec`` is used in a function and the function contains or is a\nnested block with free variables, the compiler will raise a\n``SyntaxError`` unless the exec explicitly specifies the local\nnamespace for the ``exec``. (In other words, ``exec obj`` would be\nillegal, but ``exec obj in ns`` would be legal.)\n\nThe ``eval()``, ``execfile()``, and ``input()`` functions and the\n``exec`` statement do not have access to the full environment for\nresolving names. Names may be resolved in the local and global\nnamespaces of the caller. Free variables are not resolved in the\nnearest enclosing namespace, but in the global namespace. [1] The\n``exec`` statement and the ``eval()`` and ``execfile()`` functions\nhave optional arguments to override the global and local namespace.\nIf only one namespace is specified, it is used for both.\n\n\nExceptions\n==========\n\nExceptions are a means of breaking out of the normal flow of control\nof a code block in order to handle errors or other exceptional\nconditions. An exception is *raised* at the point where the error is\ndetected; it may be *handled* by the surrounding code block or by any\ncode block that directly or indirectly invoked the code block where\nthe error occurred.\n\nThe Python interpreter raises an exception when it detects a run-time\nerror (such as division by zero). A Python program can also\nexplicitly raise an exception with the ``raise`` statement. Exception\nhandlers are specified with the ``try`` ... ``except`` statement. The\n``finally`` clause of such a statement can be used to specify cleanup\ncode which does not handle the exception, but is executed whether an\nexception occurred or not in the preceding code.\n\nPython uses the "termination" model of error handling: an exception\nhandler can find out what happened and continue execution at an outer\nlevel, but it cannot repair the cause of the error and retry the\nfailing operation (except by re-entering the offending piece of code\nfrom the top).\n\nWhen an exception is not handled at all, the interpreter terminates\nexecution of the program, or returns to its interactive main loop. In\neither case, it prints a stack backtrace, except when the exception is\n``SystemExit``.\n\nExceptions are identified by class instances. The ``except`` clause\nis selected depending on the class of the instance: it must reference\nthe class of the instance or a base class thereof. The instance can\nbe received by the handler and can carry additional information about\nthe exceptional condition.\n\nExceptions can also be identified by strings, in which case the\n``except`` clause is selected by object identity. An arbitrary value\ncan be raised along with the identifying string which can be passed to\nthe handler.\n\nNote: Messages to exceptions are not part of the Python API. Their\n contents may change from one version of Python to the next without\n warning and should not be relied on by code which will run under\n multiple versions of the interpreter.\n\nSee also the description of the ``try`` statement in section *The try\nstatement* and ``raise`` statement in section *The raise statement*.\n\n-[ Footnotes ]-\n\n[1] This limitation occurs because the code that is executed by these\n operations is not available at the time the module is compiled.\n', 'exprlists': u'\nExpression lists\n****************\n\n expression_list ::= expression ( "," expression )* [","]\n\nAn expression list containing at least one comma yields a tuple. The\nlength of the tuple is the number of expressions in the list. The\nexpressions are evaluated from left to right.\n\nThe trailing comma is required only to create a single tuple (a.k.a. a\n*singleton*); it is optional in all other cases. A single expression\nwithout a trailing comma doesn\'t create a tuple, but rather yields the\nvalue of that expression. (To create an empty tuple, use an empty pair\nof parentheses: ``()``.)\n', 'floating': u'\nFloating point literals\n***********************\n\nFloating point literals are described by the following lexical\ndefinitions:\n\n floatnumber ::= pointfloat | exponentfloat\n pointfloat ::= [intpart] fraction | intpart "."\n exponentfloat ::= (intpart | pointfloat) exponent\n intpart ::= digit+\n fraction ::= "." digit+\n exponent ::= ("e" | "E") ["+" | "-"] digit+\n\nNote that the integer and exponent parts of floating point numbers can\nlook like octal integers, but are interpreted using radix 10. For\nexample, ``077e010`` is legal, and denotes the same number as\n``77e10``. The allowed range of floating point literals is\nimplementation-dependent. Some examples of floating point literals:\n\n 3.14 10. .001 1e100 3.14e-10 0e0\n\nNote that numeric literals do not include a sign; a phrase like ``-1``\nis actually an expression composed of the unary operator ``-`` and the\nliteral ``1``.\n', 'for': u'\nThe ``for`` statement\n*********************\n\nThe ``for`` statement is used to iterate over the elements of a\nsequence (such as a string, tuple or list) or other iterable object:\n\n for_stmt ::= "for" target_list "in" expression_list ":" suite\n ["else" ":" suite]\n\nThe expression list is evaluated once; it should yield an iterable\nobject. An iterator is created for the result of the\n``expression_list``. The suite is then executed once for each item\nprovided by the iterator, in the order of ascending indices. Each\nitem in turn is assigned to the target list using the standard rules\nfor assignments, and then the suite is executed. When the items are\nexhausted (which is immediately when the sequence is empty), the suite\nin the ``else`` clause, if present, is executed, and the loop\nterminates.\n\nA ``break`` statement executed in the first suite terminates the loop\nwithout executing the ``else`` clause\'s suite. A ``continue``\nstatement executed in the first suite skips the rest of the suite and\ncontinues with the next item, or with the ``else`` clause if there was\nno next item.\n\nThe suite may assign to the variable(s) in the target list; this does\nnot affect the next item assigned to it.\n\nThe target list is not deleted when the loop is finished, but if the\nsequence is empty, it will not have been assigned to at all by the\nloop. Hint: the built-in function ``range()`` returns a sequence of\nintegers suitable to emulate the effect of Pascal\'s ``for i := a to b\ndo``; e.g., ``range(3)`` returns the list ``[0, 1, 2]``.\n\nNote: There is a subtlety when the sequence is being modified by the loop\n (this can only occur for mutable sequences, i.e. lists). An internal\n counter is used to keep track of which item is used next, and this\n is incremented on each iteration. When this counter has reached the\n length of the sequence the loop terminates. This means that if the\n suite deletes the current (or a previous) item from the sequence,\n the next item will be skipped (since it gets the index of the\n current item which has already been treated). Likewise, if the\n suite inserts an item in the sequence before the current item, the\n current item will be treated again the next time through the loop.\n This can lead to nasty bugs that can be avoided by making a\n temporary copy using a slice of the whole sequence, e.g.,\n\n for x in a[:]:\n if x < 0: a.remove(x)\n', - 'formatstrings': u'\nFormat String Syntax\n********************\n\nThe ``str.format()`` method and the ``Formatter`` class share the same\nsyntax for format strings (although in the case of ``Formatter``,\nsubclasses can define their own format string syntax.)\n\nFormat strings contain "replacement fields" surrounded by curly braces\n``{}``. Anything that is not contained in braces is considered literal\ntext, which is copied unchanged to the output. If you need to include\na brace character in the literal text, it can be escaped by doubling:\n``{{`` and ``}}``.\n\nThe grammar for a replacement field is as follows:\n\n replacement_field ::= "{" field_name ["!" conversion] [":" format_spec] "}"\n field_name ::= (identifier | integer) ("." attribute_name | "[" element_index "]")*\n attribute_name ::= identifier\n element_index ::= integer | index_string\n index_string ::= +\n conversion ::= "r" | "s"\n format_spec ::= \n\nIn less formal terms, the replacement field starts with a\n*field_name*, which can either be a number (for a positional\nargument), or an identifier (for keyword arguments). Following this\nis an optional *conversion* field, which is preceded by an exclamation\npoint ``\'!\'``, and a *format_spec*, which is preceded by a colon\n``\':\'``.\n\nThe *field_name* itself begins with either a number or a keyword. If\nit\'s a number, it refers to a positional argument, and if it\'s a\nkeyword it refers to a named keyword argument. This can be followed\nby any number of index or attribute expressions. An expression of the\nform ``\'.name\'`` selects the named attribute using ``getattr()``,\nwhile an expression of the form ``\'[index]\'`` does an index lookup\nusing ``__getitem__()``.\n\nSome simple format string examples:\n\n "First, thou shalt count to {0}" # References first positional argument\n "My quest is {name}" # References keyword argument \'name\'\n "Weight in tons {0.weight}" # \'weight\' attribute of first positional arg\n "Units destroyed: {players[0]}" # First element of keyword argument \'players\'.\n\nThe *conversion* field causes a type coercion before formatting.\nNormally, the job of formatting a value is done by the\n``__format__()`` method of the value itself. However, in some cases\nit is desirable to force a type to be formatted as a string,\noverriding its own definition of formatting. By converting the value\nto a string before calling ``__format__()``, the normal formatting\nlogic is bypassed.\n\nTwo conversion flags are currently supported: ``\'!s\'`` which calls\n``str()`` on the value, and ``\'!r\'`` which calls ``repr()``.\n\nSome examples:\n\n "Harold\'s a clever {0!s}" # Calls str() on the argument first\n "Bring out the holy {name!r}" # Calls repr() on the argument first\n\nThe *format_spec* field contains a specification of how the value\nshould be presented, including such details as field width, alignment,\npadding, decimal precision and so on. Each value type can define its\nown "formatting mini-language" or interpretation of the *format_spec*.\n\nMost built-in types support a common formatting mini-language, which\nis described in the next section.\n\nA *format_spec* field can also include nested replacement fields\nwithin it. These nested replacement fields can contain only a field\nname; conversion flags and format specifications are not allowed. The\nreplacement fields within the format_spec are substituted before the\n*format_spec* string is interpreted. This allows the formatting of a\nvalue to be dynamically specified.\n\nFor example, suppose you wanted to have a replacement field whose\nfield width is determined by another variable:\n\n "A man with two {0:{1}}".format("noses", 10)\n\nThis would first evaluate the inner replacement field, making the\nformat string effectively:\n\n "A man with two {0:10}"\n\nThen the outer replacement field would be evaluated, producing:\n\n "noses "\n\nWhich is substituted into the string, yielding:\n\n "A man with two noses "\n\n(The extra space is because we specified a field width of 10, and\nbecause left alignment is the default for strings.)\n\n\nFormat Specification Mini-Language\n==================================\n\n"Format specifications" are used within replacement fields contained\nwithin a format string to define how individual values are presented\n(see *Format String Syntax*.) They can also be passed directly to the\nbuilt-in ``format()`` function. Each formattable type may define how\nthe format specification is to be interpreted.\n\nMost built-in types implement the following options for format\nspecifications, although some of the formatting options are only\nsupported by the numeric types.\n\nA general convention is that an empty format string (``""``) produces\nthe same result as if you had called ``str()`` on the value. A non-\nempty format string typically modifies the result.\n\nThe general form of a *standard format specifier* is:\n\n format_spec ::= [[fill]align][sign][#][0][width][.precision][type]\n fill ::= \n align ::= "<" | ">" | "=" | "^"\n sign ::= "+" | "-" | " "\n width ::= integer\n precision ::= integer\n type ::= "b" | "c" | "d" | "e" | "E" | "f" | "F" | "g" | "G" | "n" | "o" | "s" | "x" | "X" | "%"\n\nThe *fill* character can be any character other than \'}\' (which\nsignifies the end of the field). The presence of a fill character is\nsignaled by the *next* character, which must be one of the alignment\noptions. If the second character of *format_spec* is not a valid\nalignment option, then it is assumed that both the fill character and\nthe alignment option are absent.\n\nThe meaning of the various alignment options is as follows:\n\n +-----------+------------------------------------------------------------+\n | Option | Meaning |\n +===========+============================================================+\n | ``\'<\'`` | Forces the field to be left-aligned within the available |\n | | space (This is the default.) |\n +-----------+------------------------------------------------------------+\n | ``\'>\'`` | Forces the field to be right-aligned within the available |\n | | space. |\n +-----------+------------------------------------------------------------+\n | ``\'=\'`` | Forces the padding to be placed after the sign (if any) |\n | | but before the digits. This is used for printing fields |\n | | in the form \'+000000120\'. This alignment option is only |\n | | valid for numeric types. |\n +-----------+------------------------------------------------------------+\n | ``\'^\'`` | Forces the field to be centered within the available |\n | | space. |\n +-----------+------------------------------------------------------------+\n\nNote that unless a minimum field width is defined, the field width\nwill always be the same size as the data to fill it, so that the\nalignment option has no meaning in this case.\n\nThe *sign* option is only valid for number types, and can be one of\nthe following:\n\n +-----------+------------------------------------------------------------+\n | Option | Meaning |\n +===========+============================================================+\n | ``\'+\'`` | indicates that a sign should be used for both positive as |\n | | well as negative numbers. |\n +-----------+------------------------------------------------------------+\n | ``\'-\'`` | indicates that a sign should be used only for negative |\n | | numbers (this is the default behavior). |\n +-----------+------------------------------------------------------------+\n | space | indicates that a leading space should be used on positive |\n | | numbers, and a minus sign on negative numbers. |\n +-----------+------------------------------------------------------------+\n\nThe ``\'#\'`` option is only valid for integers, and only for binary,\noctal, or hexadecimal output. If present, it specifies that the\noutput will be prefixed by ``\'0b\'``, ``\'0o\'``, or ``\'0x\'``,\nrespectively.\n\n*width* is a decimal integer defining the minimum field width. If not\nspecified, then the field width will be determined by the content.\n\nIf the *width* field is preceded by a zero (``\'0\'``) character, this\nenables zero-padding. This is equivalent to an *alignment* type of\n``\'=\'`` and a *fill* character of ``\'0\'``.\n\nThe *precision* is a decimal number indicating how many digits should\nbe displayed after the decimal point for a floating point value\nformatted with ``\'f\'`` and ``\'F\'``, or before and after the decimal\npoint for a floating point value formatted with ``\'g\'`` or ``\'G\'``.\nFor non-number types the field indicates the maximum field size - in\nother words, how many characters will be used from the field content.\nThe *precision* is not allowed for integer values.\n\nFinally, the *type* determines how the data should be presented.\n\nThe available string presentation types are:\n\n +-----------+------------------------------------------------------------+\n | Type | Meaning |\n +===========+============================================================+\n | ``\'s\'`` | String format. This is the default type for strings and |\n | | may be omitted. |\n +-----------+------------------------------------------------------------+\n | None | The same as ``\'s\'``. |\n +-----------+------------------------------------------------------------+\n\nThe available integer presentation types are:\n\n +-----------+------------------------------------------------------------+\n | Type | Meaning |\n +===========+============================================================+\n | ``\'b\'`` | Binary format. Outputs the number in base 2. |\n +-----------+------------------------------------------------------------+\n | ``\'c\'`` | Character. Converts the integer to the corresponding |\n | | unicode character before printing. |\n +-----------+------------------------------------------------------------+\n | ``\'d\'`` | Decimal Integer. Outputs the number in base 10. |\n +-----------+------------------------------------------------------------+\n | ``\'o\'`` | Octal format. Outputs the number in base 8. |\n +-----------+------------------------------------------------------------+\n | ``\'x\'`` | Hex format. Outputs the number in base 16, using lower- |\n | | case letters for the digits above 9. |\n +-----------+------------------------------------------------------------+\n | ``\'X\'`` | Hex format. Outputs the number in base 16, using upper- |\n | | case letters for the digits above 9. |\n +-----------+------------------------------------------------------------+\n | ``\'n\'`` | Number. This is the same as ``\'d\'``, except that it uses |\n | | the current locale setting to insert the appropriate |\n | | number separator characters. |\n +-----------+------------------------------------------------------------+\n | None | The same as ``\'d\'``. |\n +-----------+------------------------------------------------------------+\n\nIn addition to the above presentation types, integers can be formatted\nwith the floating point presentation types listed below (except\n``\'n\'`` and None). When doing so, ``float()`` is used to convert the\ninteger to a floating point number before formatting.\n\nThe available presentation types for floating point and decimal values\nare:\n\n +-----------+------------------------------------------------------------+\n | Type | Meaning |\n +===========+============================================================+\n | ``\'e\'`` | Exponent notation. Prints the number in scientific |\n | | notation using the letter \'e\' to indicate the exponent. |\n +-----------+------------------------------------------------------------+\n | ``\'E\'`` | Exponent notation. Same as ``\'e\'`` except it uses an upper |\n | | case \'E\' as the separator character. |\n +-----------+------------------------------------------------------------+\n | ``\'f\'`` | Fixed point. Displays the number as a fixed-point number. |\n +-----------+------------------------------------------------------------+\n | ``\'F\'`` | Fixed point. Same as ``\'f\'``. |\n +-----------+------------------------------------------------------------+\n | ``\'g\'`` | General format. For a given precision ``p >= 1``, this |\n | | rounds the number to ``p`` significant digits and then |\n | | formats the result in either fixed-point format or in |\n | | scientific notation, depending on its magnitude. The |\n | | precise rules are as follows: suppose that the result |\n | | formatted with presentation type ``\'e\'`` and precision |\n | | ``p-1`` would have exponent ``exp``. Then if ``-4 <= exp |\n | | < p``, the number is formatted with presentation type |\n | | ``\'f\'`` and precision ``p-1-exp``. Otherwise, the number |\n | | is formatted with presentation type ``\'e\'`` and precision |\n | | ``p-1``. In both cases insignificant trailing zeros are |\n | | removed from the significand, and the decimal point is |\n | | also removed if there are no remaining digits following |\n | | it. Postive and negative infinity, positive and negative |\n | | zero, and nans, are formatted as ``inf``, ``-inf``, ``0``, |\n | | ``-0`` and ``nan`` respectively, regardless of the |\n | | precision. A precision of ``0`` is treated as equivalent |\n | | to a precision of ``1``. |\n +-----------+------------------------------------------------------------+\n | ``\'G\'`` | General format. Same as ``\'g\'`` except switches to ``\'E\'`` |\n | | if the number gets too large. The representations of |\n | | infinity and NaN are uppercased, too. |\n +-----------+------------------------------------------------------------+\n | ``\'n\'`` | Number. This is the same as ``\'g\'``, except that it uses |\n | | the current locale setting to insert the appropriate |\n | | number separator characters. |\n +-----------+------------------------------------------------------------+\n | ``\'%\'`` | Percentage. Multiplies the number by 100 and displays in |\n | | fixed (``\'f\'``) format, followed by a percent sign. |\n +-----------+------------------------------------------------------------+\n | None | The same as ``\'g\'``. |\n +-----------+------------------------------------------------------------+\n', + 'formatstrings': u'\nFormat String Syntax\n********************\n\nThe ``str.format()`` method and the ``Formatter`` class share the same\nsyntax for format strings (although in the case of ``Formatter``,\nsubclasses can define their own format string syntax).\n\nFormat strings contain "replacement fields" surrounded by curly braces\n``{}``. Anything that is not contained in braces is considered literal\ntext, which is copied unchanged to the output. If you need to include\na brace character in the literal text, it can be escaped by doubling:\n``{{`` and ``}}``.\n\nThe grammar for a replacement field is as follows:\n\n replacement_field ::= "{" field_name ["!" conversion] [":" format_spec] "}"\n field_name ::= (identifier | integer) ("." attribute_name | "[" element_index "]")*\n attribute_name ::= identifier\n element_index ::= integer | index_string\n index_string ::= +\n conversion ::= "r" | "s"\n format_spec ::= \n\nIn less formal terms, the replacement field starts with a\n*field_name*, which can either be a number (for a positional\nargument), or an identifier (for keyword arguments). Following this\nis an optional *conversion* field, which is preceded by an exclamation\npoint ``\'!\'``, and a *format_spec*, which is preceded by a colon\n``\':\'``.\n\nSee also the *Format Specification Mini-Language* section.\n\nThe *field_name* itself begins with either a number or a keyword. If\nit\'s a number, it refers to a positional argument, and if it\'s a\nkeyword it refers to a named keyword argument. This can be followed\nby any number of index or attribute expressions. An expression of the\nform ``\'.name\'`` selects the named attribute using ``getattr()``,\nwhile an expression of the form ``\'[index]\'`` does an index lookup\nusing ``__getitem__()``.\n\nSome simple format string examples:\n\n "First, thou shalt count to {0}" # References first positional argument\n "My quest is {name}" # References keyword argument \'name\'\n "Weight in tons {0.weight}" # \'weight\' attribute of first positional arg\n "Units destroyed: {players[0]}" # First element of keyword argument \'players\'.\n\nThe *conversion* field causes a type coercion before formatting.\nNormally, the job of formatting a value is done by the\n``__format__()`` method of the value itself. However, in some cases\nit is desirable to force a type to be formatted as a string,\noverriding its own definition of formatting. By converting the value\nto a string before calling ``__format__()``, the normal formatting\nlogic is bypassed.\n\nTwo conversion flags are currently supported: ``\'!s\'`` which calls\n``str()`` on the value, and ``\'!r\'`` which calls ``repr()``.\n\nSome examples:\n\n "Harold\'s a clever {0!s}" # Calls str() on the argument first\n "Bring out the holy {name!r}" # Calls repr() on the argument first\n\nThe *format_spec* field contains a specification of how the value\nshould be presented, including such details as field width, alignment,\npadding, decimal precision and so on. Each value type can define its\nown "formatting mini-language" or interpretation of the *format_spec*.\n\nMost built-in types support a common formatting mini-language, which\nis described in the next section.\n\nA *format_spec* field can also include nested replacement fields\nwithin it. These nested replacement fields can contain only a field\nname; conversion flags and format specifications are not allowed. The\nreplacement fields within the format_spec are substituted before the\n*format_spec* string is interpreted. This allows the formatting of a\nvalue to be dynamically specified.\n\nSee the *Format examples* section for some examples.\n\n\nFormat Specification Mini-Language\n==================================\n\n"Format specifications" are used within replacement fields contained\nwithin a format string to define how individual values are presented\n(see *Format String Syntax*). They can also be passed directly to the\nbuilt-in ``format()`` function. Each formattable type may define how\nthe format specification is to be interpreted.\n\nMost built-in types implement the following options for format\nspecifications, although some of the formatting options are only\nsupported by the numeric types.\n\nA general convention is that an empty format string (``""``) produces\nthe same result as if you had called ``str()`` on the value. A non-\nempty format string typically modifies the result.\n\nThe general form of a *standard format specifier* is:\n\n format_spec ::= [[fill]align][sign][#][0][width][.precision][type]\n fill ::= \n align ::= "<" | ">" | "=" | "^"\n sign ::= "+" | "-" | " "\n width ::= integer\n precision ::= integer\n type ::= "b" | "c" | "d" | "e" | "E" | "f" | "F" | "g" | "G" | "n" | "o" | "s" | "x" | "X" | "%"\n\nThe *fill* character can be any character other than \'}\' (which\nsignifies the end of the field). The presence of a fill character is\nsignaled by the *next* character, which must be one of the alignment\noptions. If the second character of *format_spec* is not a valid\nalignment option, then it is assumed that both the fill character and\nthe alignment option are absent.\n\nThe meaning of the various alignment options is as follows:\n\n +-----------+------------------------------------------------------------+\n | Option | Meaning |\n +===========+============================================================+\n | ``\'<\'`` | Forces the field to be left-aligned within the available |\n | | space (this is the default). |\n +-----------+------------------------------------------------------------+\n | ``\'>\'`` | Forces the field to be right-aligned within the available |\n | | space. |\n +-----------+------------------------------------------------------------+\n | ``\'=\'`` | Forces the padding to be placed after the sign (if any) |\n | | but before the digits. This is used for printing fields |\n | | in the form \'+000000120\'. This alignment option is only |\n | | valid for numeric types. |\n +-----------+------------------------------------------------------------+\n | ``\'^\'`` | Forces the field to be centered within the available |\n | | space. |\n +-----------+------------------------------------------------------------+\n\nNote that unless a minimum field width is defined, the field width\nwill always be the same size as the data to fill it, so that the\nalignment option has no meaning in this case.\n\nThe *sign* option is only valid for number types, and can be one of\nthe following:\n\n +-----------+------------------------------------------------------------+\n | Option | Meaning |\n +===========+============================================================+\n | ``\'+\'`` | indicates that a sign should be used for both positive as |\n | | well as negative numbers. |\n +-----------+------------------------------------------------------------+\n | ``\'-\'`` | indicates that a sign should be used only for negative |\n | | numbers (this is the default behavior). |\n +-----------+------------------------------------------------------------+\n | space | indicates that a leading space should be used on positive |\n | | numbers, and a minus sign on negative numbers. |\n +-----------+------------------------------------------------------------+\n\nThe ``\'#\'`` option is only valid for integers, and only for binary,\noctal, or hexadecimal output. If present, it specifies that the\noutput will be prefixed by ``\'0b\'``, ``\'0o\'``, or ``\'0x\'``,\nrespectively.\n\n*width* is a decimal integer defining the minimum field width. If not\nspecified, then the field width will be determined by the content.\n\nIf the *width* field is preceded by a zero (``\'0\'``) character, this\nenables zero-padding. This is equivalent to an *alignment* type of\n``\'=\'`` and a *fill* character of ``\'0\'``.\n\nThe *precision* is a decimal number indicating how many digits should\nbe displayed after the decimal point for a floating point value\nformatted with ``\'f\'`` and ``\'F\'``, or before and after the decimal\npoint for a floating point value formatted with ``\'g\'`` or ``\'G\'``.\nFor non-number types the field indicates the maximum field size - in\nother words, how many characters will be used from the field content.\nThe *precision* is not allowed for integer values.\n\nFinally, the *type* determines how the data should be presented.\n\nThe available string presentation types are:\n\n +-----------+------------------------------------------------------------+\n | Type | Meaning |\n +===========+============================================================+\n | ``\'s\'`` | String format. This is the default type for strings and |\n | | may be omitted. |\n +-----------+------------------------------------------------------------+\n | None | The same as ``\'s\'``. |\n +-----------+------------------------------------------------------------+\n\nThe available integer presentation types are:\n\n +-----------+------------------------------------------------------------+\n | Type | Meaning |\n +===========+============================================================+\n | ``\'b\'`` | Binary format. Outputs the number in base 2. |\n +-----------+------------------------------------------------------------+\n | ``\'c\'`` | Character. Converts the integer to the corresponding |\n | | unicode character before printing. |\n +-----------+------------------------------------------------------------+\n | ``\'d\'`` | Decimal Integer. Outputs the number in base 10. |\n +-----------+------------------------------------------------------------+\n | ``\'o\'`` | Octal format. Outputs the number in base 8. |\n +-----------+------------------------------------------------------------+\n | ``\'x\'`` | Hex format. Outputs the number in base 16, using lower- |\n | | case letters for the digits above 9. |\n +-----------+------------------------------------------------------------+\n | ``\'X\'`` | Hex format. Outputs the number in base 16, using upper- |\n | | case letters for the digits above 9. |\n +-----------+------------------------------------------------------------+\n | ``\'n\'`` | Number. This is the same as ``\'d\'``, except that it uses |\n | | the current locale setting to insert the appropriate |\n | | number separator characters. |\n +-----------+------------------------------------------------------------+\n | None | The same as ``\'d\'``. |\n +-----------+------------------------------------------------------------+\n\nIn addition to the above presentation types, integers can be formatted\nwith the floating point presentation types listed below (except\n``\'n\'`` and None). When doing so, ``float()`` is used to convert the\ninteger to a floating point number before formatting.\n\nThe available presentation types for floating point and decimal values\nare:\n\n +-----------+------------------------------------------------------------+\n | Type | Meaning |\n +===========+============================================================+\n | ``\'e\'`` | Exponent notation. Prints the number in scientific |\n | | notation using the letter \'e\' to indicate the exponent. |\n +-----------+------------------------------------------------------------+\n | ``\'E\'`` | Exponent notation. Same as ``\'e\'`` except it uses an upper |\n | | case \'E\' as the separator character. |\n +-----------+------------------------------------------------------------+\n | ``\'f\'`` | Fixed point. Displays the number as a fixed-point number. |\n +-----------+------------------------------------------------------------+\n | ``\'F\'`` | Fixed point. Same as ``\'f\'``. |\n +-----------+------------------------------------------------------------+\n | ``\'g\'`` | General format. For a given precision ``p >= 1``, this |\n | | rounds the number to ``p`` significant digits and then |\n | | formats the result in either fixed-point format or in |\n | | scientific notation, depending on its magnitude. The |\n | | precise rules are as follows: suppose that the result |\n | | formatted with presentation type ``\'e\'`` and precision |\n | | ``p-1`` would have exponent ``exp``. Then if ``-4 <= exp |\n | | < p``, the number is formatted with presentation type |\n | | ``\'f\'`` and precision ``p-1-exp``. Otherwise, the number |\n | | is formatted with presentation type ``\'e\'`` and precision |\n | | ``p-1``. In both cases insignificant trailing zeros are |\n | | removed from the significand, and the decimal point is |\n | | also removed if there are no remaining digits following |\n | | it. Postive and negative infinity, positive and negative |\n | | zero, and nans, are formatted as ``inf``, ``-inf``, ``0``, |\n | | ``-0`` and ``nan`` respectively, regardless of the |\n | | precision. A precision of ``0`` is treated as equivalent |\n | | to a precision of ``1``. |\n +-----------+------------------------------------------------------------+\n | ``\'G\'`` | General format. Same as ``\'g\'`` except switches to ``\'E\'`` |\n | | if the number gets too large. The representations of |\n | | infinity and NaN are uppercased, too. |\n +-----------+------------------------------------------------------------+\n | ``\'n\'`` | Number. This is the same as ``\'g\'``, except that it uses |\n | | the current locale setting to insert the appropriate |\n | | number separator characters. |\n +-----------+------------------------------------------------------------+\n | ``\'%\'`` | Percentage. Multiplies the number by 100 and displays in |\n | | fixed (``\'f\'``) format, followed by a percent sign. |\n +-----------+------------------------------------------------------------+\n | None | The same as ``\'g\'``. |\n +-----------+------------------------------------------------------------+\n\n\nFormat examples\n===============\n\nThis section contains examples of the new format syntax and comparison\nwith the old ``%``-formatting.\n\nIn most of the cases the syntax is similar to the old\n``%``-formatting, with the addition of the ``{}`` and with ``:`` used\ninstead of ``%``. For example, ``\'%03.2f\'`` can be translated to\n``\'{0:03.2f}\'``.\n\nThe new format syntax also supports new and different options, shown\nin the follow examples.\n\nAccessing arguments by position:\n\n >>> \'{0}, {1}, {2}\'.format(\'a\', \'b\', \'c\')\n \'a, b, c\'\n >>> \'{2}, {1}, {0}\'.format(\'a\', \'b\', \'c\')\n \'c, b, a\'\n >>> \'{2}, {1}, {0}\'.format(*\'abc\') # unpacking argument sequence\n \'c, b, a\'\n >>> \'{0}{1}{0}\'.format(\'abra\', \'cad\') # arguments\' indices can be repeated\n \'abracadabra\'\n\nAccessing arguments by name:\n\n >>> \'Coordinates: {latitude}, {longitude}\'.format(latitude=\'37.24N\', longitude=\'-115.81W\')\n \'Coordinates: 37.24N, -115.81W\'\n >>> coord = {\'latitude\': \'37.24N\', \'longitude\': \'-115.81W\'}\n >>> \'Coordinates: {latitude}, {longitude}\'.format(**coord)\n \'Coordinates: 37.24N, -115.81W\'\n\nAccessing arguments\' attributes:\n\n >>> c = 3-5j\n >>> (\'The complex number {0} is formed from the real part {0.real} \'\n ... \'and the imaginary part {0.imag}.\').format(c)\n \'The complex number (3-5j) is formed from the real part 3.0 and the imaginary part -5.0.\'\n >>> class Point(object):\n ... def __init__(self, x, y):\n ... self.x, self.y = x, y\n ... def __str__(self):\n ... return \'Point({self.x}, {self.y})\'.format(self=self)\n ...\n >>> str(Point(4, 2))\n \'Point(4, 2)\'\n\nAccessing arguments\' items:\n\n >>> coord = (3, 5)\n >>> \'X: {0[0]}; Y: {0[1]}\'.format(coord)\n \'X: 3; Y: 5\'\n\nReplacing ``%s`` and ``%r``:\n\n >>> "repr() shows quotes: {0!r}; str() doesn\'t: {1!s}".format(\'test1\', \'test2\')\n "repr() shows quotes: \'test1\'; str() doesn\'t: test2"\n\nAligning the text and specifying a width:\n\n >>> \'{0:<30}\'.format(\'left aligned\')\n \'left aligned \'\n >>> \'{0:>30}\'.format(\'right aligned\')\n \' right aligned\'\n >>> \'{0:^30}\'.format(\'centered\')\n \' centered \'\n >>> \'{0:*^30}\'.format(\'centered\') # use \'*\' as a fill char\n \'***********centered***********\'\n\nReplacing ``%+f``, ``%-f``, and ``% f`` and specifying a sign:\n\n >>> \'{0:+f}; {0:+f}\'.format(3.14, -3.14) # show it always\n \'+3.140000; -3.140000\'\n >>> \'{0: f}; {0: f}\'.format(3.14, -3.14) # show a space for positive numbers\n \' 3.140000; -3.140000\'\n >>> \'{0:-f}; {0:-f}\'.format(3.14, -3.14) # show only the minus -- same as \'{0:f}; {0:f}\'\n \'3.140000; -3.140000\'\n\nReplacing ``%x`` and ``%o`` and converting the value to different\nbases:\n\n >>> # format also supports binary numbers\n >>> "int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}".format(42)\n \'int: 42; hex: 2a; oct: 52; bin: 101010\'\n >>> # with 0x, 0o, or 0b as prefix:\n >>> "int: {0:d}; hex: {0:#x}; oct: {0:#o}; bin: {0:#b}".format(42)\n \'int: 42; hex: 0x2a; oct: 0o52; bin: 0b101010\'\n\nExpressing a percentage:\n\n >>> points = 19.5\n >>> total = 22\n >>> \'Correct answers: {0:.2%}.\'.format(points/total)\n \'Correct answers: 88.64%\'\n\nUsing type-specific formatting:\n\n >>> import datetime\n >>> d = datetime.datetime(2010, 7, 4, 12, 15, 58)\n >>> \'{0:%Y-%m-%d %H:%M:%S}\'.format(d)\n \'2010-07-04 12:15:58\'\n\nNesting arguments and more complex examples:\n\n >>> for align, text in zip(\'<^>\', [\'left\', \'center\', \'right\']):\n ... \'{0:{align}{fill}16}\'.format(text, fill=align, align=align)\n ...\n \'left<<<<<<<<<<<<\'\n \'^^^^^center^^^^^\'\n \'>>>>>>>>>>>right\'\n >>>\n >>> octets = [192, 168, 0, 1]\n >>> \'{0:02X}{1:02X}{2:02X}{3:02X}\'.format(*octets)\n \'C0A80001\'\n >>> int(_, 16)\n 3232235521\n >>>\n >>> width = 5\n >>> for num in range(5,12):\n ... for base in \'dXob\':\n ... print \'{0:{width}{base}}\'.format(num, base=base, width=width),\n ... print\n ...\n 5 5 5 101\n 6 6 6 110\n 7 7 7 111\n 8 8 10 1000\n 9 9 11 1001\n 10 A 12 1010\n 11 B 13 1011\n', 'function': u'\nFunction definitions\n********************\n\nA function definition defines a user-defined function object (see\nsection *The standard type hierarchy*):\n\n decorated ::= decorators (classdef | funcdef)\n decorators ::= decorator+\n decorator ::= "@" dotted_name ["(" [argument_list [","]] ")"] NEWLINE\n funcdef ::= "def" funcname "(" [parameter_list] ")" ":" suite\n dotted_name ::= identifier ("." identifier)*\n parameter_list ::= (defparameter ",")*\n ( "*" identifier [, "**" identifier]\n | "**" identifier\n | defparameter [","] )\n defparameter ::= parameter ["=" expression]\n sublist ::= parameter ("," parameter)* [","]\n parameter ::= identifier | "(" sublist ")"\n funcname ::= identifier\n\nA function definition is an executable statement. Its execution binds\nthe function name in the current local namespace to a function object\n(a wrapper around the executable code for the function). This\nfunction object contains a reference to the current global namespace\nas the global namespace to be used when the function is called.\n\nThe function definition does not execute the function body; this gets\nexecuted only when the function is called. [3]\n\nA function definition may be wrapped by one or more *decorator*\nexpressions. Decorator expressions are evaluated when the function is\ndefined, in the scope that contains the function definition. The\nresult must be a callable, which is invoked with the function object\nas the only argument. The returned value is bound to the function name\ninstead of the function object. Multiple decorators are applied in\nnested fashion. For example, the following code:\n\n @f1(arg)\n @f2\n def func(): pass\n\nis equivalent to:\n\n def func(): pass\n func = f1(arg)(f2(func))\n\nWhen one or more top-level parameters have the form *parameter* ``=``\n*expression*, the function is said to have "default parameter values."\nFor a parameter with a default value, the corresponding argument may\nbe omitted from a call, in which case the parameter\'s default value is\nsubstituted. If a parameter has a default value, all following\nparameters must also have a default value --- this is a syntactic\nrestriction that is not expressed by the grammar.\n\n**Default parameter values are evaluated when the function definition\nis executed.** This means that the expression is evaluated once, when\nthe function is defined, and that that same "pre-computed" value is\nused for each call. This is especially important to understand when a\ndefault parameter is a mutable object, such as a list or a dictionary:\nif the function modifies the object (e.g. by appending an item to a\nlist), the default value is in effect modified. This is generally not\nwhat was intended. A way around this is to use ``None`` as the\ndefault, and explicitly test for it in the body of the function, e.g.:\n\n def whats_on_the_telly(penguin=None):\n if penguin is None:\n penguin = []\n penguin.append("property of the zoo")\n return penguin\n\nFunction call semantics are described in more detail in section\n*Calls*. A function call always assigns values to all parameters\nmentioned in the parameter list, either from position arguments, from\nkeyword arguments, or from default values. If the form\n"``*identifier``" is present, it is initialized to a tuple receiving\nany excess positional parameters, defaulting to the empty tuple. If\nthe form "``**identifier``" is present, it is initialized to a new\ndictionary receiving any excess keyword arguments, defaulting to a new\nempty dictionary.\n\nIt is also possible to create anonymous functions (functions not bound\nto a name), for immediate use in expressions. This uses lambda forms,\ndescribed in section *Lambdas*. Note that the lambda form is merely a\nshorthand for a simplified function definition; a function defined in\na "``def``" statement can be passed around or assigned to another name\njust like a function defined by a lambda form. The "``def``" form is\nactually more powerful since it allows the execution of multiple\nstatements.\n\n**Programmer\'s note:** Functions are first-class objects. A "``def``"\nform executed inside a function definition defines a local function\nthat can be returned or passed around. Free variables used in the\nnested function can access the local variables of the function\ncontaining the def. See section *Naming and binding* for details.\n', 'global': u'\nThe ``global`` statement\n************************\n\n global_stmt ::= "global" identifier ("," identifier)*\n\nThe ``global`` statement is a declaration which holds for the entire\ncurrent code block. It means that the listed identifiers are to be\ninterpreted as globals. It would be impossible to assign to a global\nvariable without ``global``, although free variables may refer to\nglobals without being declared global.\n\nNames listed in a ``global`` statement must not be used in the same\ncode block textually preceding that ``global`` statement.\n\nNames listed in a ``global`` statement must not be defined as formal\nparameters or in a ``for`` loop control target, ``class`` definition,\nfunction definition, or ``import`` statement.\n\n**CPython implementation detail:** The current implementation does not\nenforce the latter two restrictions, but programs should not abuse\nthis freedom, as future implementations may enforce them or silently\nchange the meaning of the program.\n\n**Programmer\'s note:** the ``global`` is a directive to the parser.\nIt applies only to code parsed at the same time as the ``global``\nstatement. In particular, a ``global`` statement contained in an\n``exec`` statement does not affect the code block *containing* the\n``exec`` statement, and code contained in an ``exec`` statement is\nunaffected by ``global`` statements in the code containing the\n``exec`` statement. The same applies to the ``eval()``,\n``execfile()`` and ``compile()`` functions.\n', - 'id-classes': u'\nReserved classes of identifiers\n*******************************\n\nCertain classes of identifiers (besides keywords) have special\nmeanings. These classes are identified by the patterns of leading and\ntrailing underscore characters:\n\n``_*``\n Not imported by ``from module import *``. The special identifier\n ``_`` is used in the interactive interpreter to store the result of\n the last evaluation; it is stored in the ``__builtin__`` module.\n When not in interactive mode, ``_`` has no special meaning and is\n not defined. See section *The import statement*.\n\n Note: The name ``_`` is often used in conjunction with\n internationalization; refer to the documentation for the\n ``gettext`` module for more information on this convention.\n\n``__*__``\n System-defined names. These names are defined by the interpreter\n and its implementation (including the standard library);\n applications should not expect to define additional names using\n this convention. The set of names of this class defined by Python\n may be extended in future versions. See section *Special method\n names*.\n\n``__*``\n Class-private names. Names in this category, when used within the\n context of a class definition, are re-written to use a mangled form\n to help avoid name clashes between "private" attributes of base and\n derived classes. See section *Identifiers (Names)*.\n', - 'identifiers': u'\nIdentifiers and keywords\n************************\n\nIdentifiers (also referred to as *names*) are described by the\nfollowing lexical definitions:\n\n identifier ::= (letter|"_") (letter | digit | "_")*\n letter ::= lowercase | uppercase\n lowercase ::= "a"..."z"\n uppercase ::= "A"..."Z"\n digit ::= "0"..."9"\n\nIdentifiers are unlimited in length. Case is significant.\n\n\nKeywords\n========\n\nThe following identifiers are used as reserved words, or *keywords* of\nthe language, and cannot be used as ordinary identifiers. They must\nbe spelled exactly as written here:\n\n and del from not while\n as elif global or with\n assert else if pass yield\n break except import print\n class exec in raise\n continue finally is return\n def for lambda try\n\nChanged in version 2.4: ``None`` became a constant and is now\nrecognized by the compiler as a name for the built-in object ``None``.\nAlthough it is not a keyword, you cannot assign a different object to\nit.\n\nChanged in version 2.5: Both ``as`` and ``with`` are only recognized\nwhen the ``with_statement`` future feature has been enabled. It will\nalways be enabled in Python 2.6. See section *The with statement* for\ndetails. Note that using ``as`` and ``with`` as identifiers will\nalways issue a warning, even when the ``with_statement`` future\ndirective is not in effect.\n\n\nReserved classes of identifiers\n===============================\n\nCertain classes of identifiers (besides keywords) have special\nmeanings. These classes are identified by the patterns of leading and\ntrailing underscore characters:\n\n``_*``\n Not imported by ``from module import *``. The special identifier\n ``_`` is used in the interactive interpreter to store the result of\n the last evaluation; it is stored in the ``__builtin__`` module.\n When not in interactive mode, ``_`` has no special meaning and is\n not defined. See section *The import statement*.\n\n Note: The name ``_`` is often used in conjunction with\n internationalization; refer to the documentation for the\n ``gettext`` module for more information on this convention.\n\n``__*__``\n System-defined names. These names are defined by the interpreter\n and its implementation (including the standard library);\n applications should not expect to define additional names using\n this convention. The set of names of this class defined by Python\n may be extended in future versions. See section *Special method\n names*.\n\n``__*``\n Class-private names. Names in this category, when used within the\n context of a class definition, are re-written to use a mangled form\n to help avoid name clashes between "private" attributes of base and\n derived classes. See section *Identifiers (Names)*.\n', + 'id-classes': u'\nReserved classes of identifiers\n*******************************\n\nCertain classes of identifiers (besides keywords) have special\nmeanings. These classes are identified by the patterns of leading and\ntrailing underscore characters:\n\n``_*``\n Not imported by ``from module import *``. The special identifier\n ``_`` is used in the interactive interpreter to store the result of\n the last evaluation; it is stored in the ``__builtin__`` module.\n When not in interactive mode, ``_`` has no special meaning and is\n not defined. See section *The import statement*.\n\n Note: The name ``_`` is often used in conjunction with\n internationalization; refer to the documentation for the\n ``gettext`` module for more information on this convention.\n\n``__*__``\n System-defined names. These names are defined by the interpreter\n and its implementation (including the standard library). Current\n system names are discussed in the *Special method names* section\n and elsewhere. More will likely be defined in future versions of\n Python. *Any* use of ``__*__`` names, in any context, that does\n not follow explicitly documented use, is subject to breakage\n without warning.\n\n``__*``\n Class-private names. Names in this category, when used within the\n context of a class definition, are re-written to use a mangled form\n to help avoid name clashes between "private" attributes of base and\n derived classes. See section *Identifiers (Names)*.\n', + 'identifiers': u'\nIdentifiers and keywords\n************************\n\nIdentifiers (also referred to as *names*) are described by the\nfollowing lexical definitions:\n\n identifier ::= (letter|"_") (letter | digit | "_")*\n letter ::= lowercase | uppercase\n lowercase ::= "a"..."z"\n uppercase ::= "A"..."Z"\n digit ::= "0"..."9"\n\nIdentifiers are unlimited in length. Case is significant.\n\n\nKeywords\n========\n\nThe following identifiers are used as reserved words, or *keywords* of\nthe language, and cannot be used as ordinary identifiers. They must\nbe spelled exactly as written here:\n\n and del from not while\n as elif global or with\n assert else if pass yield\n break except import print\n class exec in raise\n continue finally is return\n def for lambda try\n\nChanged in version 2.4: ``None`` became a constant and is now\nrecognized by the compiler as a name for the built-in object ``None``.\nAlthough it is not a keyword, you cannot assign a different object to\nit.\n\nChanged in version 2.5: Both ``as`` and ``with`` are only recognized\nwhen the ``with_statement`` future feature has been enabled. It will\nalways be enabled in Python 2.6. See section *The with statement* for\ndetails. Note that using ``as`` and ``with`` as identifiers will\nalways issue a warning, even when the ``with_statement`` future\ndirective is not in effect.\n\n\nReserved classes of identifiers\n===============================\n\nCertain classes of identifiers (besides keywords) have special\nmeanings. These classes are identified by the patterns of leading and\ntrailing underscore characters:\n\n``_*``\n Not imported by ``from module import *``. The special identifier\n ``_`` is used in the interactive interpreter to store the result of\n the last evaluation; it is stored in the ``__builtin__`` module.\n When not in interactive mode, ``_`` has no special meaning and is\n not defined. See section *The import statement*.\n\n Note: The name ``_`` is often used in conjunction with\n internationalization; refer to the documentation for the\n ``gettext`` module for more information on this convention.\n\n``__*__``\n System-defined names. These names are defined by the interpreter\n and its implementation (including the standard library). Current\n system names are discussed in the *Special method names* section\n and elsewhere. More will likely be defined in future versions of\n Python. *Any* use of ``__*__`` names, in any context, that does\n not follow explicitly documented use, is subject to breakage\n without warning.\n\n``__*``\n Class-private names. Names in this category, when used within the\n context of a class definition, are re-written to use a mangled form\n to help avoid name clashes between "private" attributes of base and\n derived classes. See section *Identifiers (Names)*.\n', 'if': u'\nThe ``if`` statement\n********************\n\nThe ``if`` statement is used for conditional execution:\n\n if_stmt ::= "if" expression ":" suite\n ( "elif" expression ":" suite )*\n ["else" ":" suite]\n\nIt selects exactly one of the suites by evaluating the expressions one\nby one until one is found to be true (see section *Boolean operations*\nfor the definition of true and false); then that suite is executed\n(and no other part of the ``if`` statement is executed or evaluated).\nIf all expressions are false, the suite of the ``else`` clause, if\npresent, is executed.\n', 'imaginary': u'\nImaginary literals\n******************\n\nImaginary literals are described by the following lexical definitions:\n\n imagnumber ::= (floatnumber | intpart) ("j" | "J")\n\nAn imaginary literal yields a complex number with a real part of 0.0.\nComplex numbers are represented as a pair of floating point numbers\nand have the same restrictions on their range. To create a complex\nnumber with a nonzero real part, add a floating point number to it,\ne.g., ``(3+4j)``. Some examples of imaginary literals:\n\n 3.14j 10.j 10j .001j 1e100j 3.14e-10j\n', 'import': u'\nThe ``import`` statement\n************************\n\n import_stmt ::= "import" module ["as" name] ( "," module ["as" name] )*\n | "from" relative_module "import" identifier ["as" name]\n ( "," identifier ["as" name] )*\n | "from" relative_module "import" "(" identifier ["as" name]\n ( "," identifier ["as" name] )* [","] ")"\n | "from" module "import" "*"\n module ::= (identifier ".")* identifier\n relative_module ::= "."* module | "."+\n name ::= identifier\n\nImport statements are executed in two steps: (1) find a module, and\ninitialize it if necessary; (2) define a name or names in the local\nnamespace (of the scope where the ``import`` statement occurs). The\nstatement comes in two forms differing on whether it uses the ``from``\nkeyword. The first form (without ``from``) repeats these steps for\neach identifier in the list. The form with ``from`` performs step (1)\nonce, and then performs step (2) repeatedly.\n\nTo understand how step (1) occurs, one must first understand how\nPython handles hierarchical naming of modules. To help organize\nmodules and provide a hierarchy in naming, Python has a concept of\npackages. A package can contain other packages and modules while\nmodules cannot contain other modules or packages. From a file system\nperspective, packages are directories and modules are files. The\noriginal specification for packages is still available to read,\nalthough minor details have changed since the writing of that\ndocument.\n\nOnce the name of the module is known (unless otherwise specified, the\nterm "module" will refer to both packages and modules), searching for\nthe module or package can begin. The first place checked is\n``sys.modules``, the cache of all modules that have been imported\npreviously. If the module is found there then it is used in step (2)\nof import.\n\nIf the module is not found in the cache, then ``sys.meta_path`` is\nsearched (the specification for ``sys.meta_path`` can be found in\n**PEP 302**). The object is a list of *finder* objects which are\nqueried in order as to whether they know how to load the module by\ncalling their ``find_module()`` method with the name of the module. If\nthe module happens to be contained within a package (as denoted by the\nexistence of a dot in the name), then a second argument to\n``find_module()`` is given as the value of the ``__path__`` attribute\nfrom the parent package (everything up to the last dot in the name of\nthe module being imported). If a finder can find the module it returns\na *loader* (discussed later) or returns ``None``.\n\nIf none of the finders on ``sys.meta_path`` are able to find the\nmodule then some implicitly defined finders are queried.\nImplementations of Python vary in what implicit meta path finders are\ndefined. The one they all do define, though, is one that handles\n``sys.path_hooks``, ``sys.path_importer_cache``, and ``sys.path``.\n\nThe implicit finder searches for the requested module in the "paths"\nspecified in one of two places ("paths" do not have to be file system\npaths). If the module being imported is supposed to be contained\nwithin a package then the second argument passed to ``find_module()``,\n``__path__`` on the parent package, is used as the source of paths. If\nthe module is not contained in a package then ``sys.path`` is used as\nthe source of paths.\n\nOnce the source of paths is chosen it is iterated over to find a\nfinder that can handle that path. The dict at\n``sys.path_importer_cache`` caches finders for paths and is checked\nfor a finder. If the path does not have a finder cached then\n``sys.path_hooks`` is searched by calling each object in the list with\na single argument of the path, returning a finder or raises\n``ImportError``. If a finder is returned then it is cached in\n``sys.path_importer_cache`` and then used for that path entry. If no\nfinder can be found but the path exists then a value of ``None`` is\nstored in ``sys.path_importer_cache`` to signify that an implicit,\nfile-based finder that handles modules stored as individual files\nshould be used for that path. If the path does not exist then a finder\nwhich always returns ``None`` is placed in the cache for the path.\n\nIf no finder can find the module then ``ImportError`` is raised.\nOtherwise some finder returned a loader whose ``load_module()`` method\nis called with the name of the module to load (see **PEP 302** for the\noriginal definition of loaders). A loader has several responsibilities\nto perform on a module it loads. First, if the module already exists\nin ``sys.modules`` (a possibility if the loader is called outside of\nthe import machinery) then it is to use that module for initialization\nand not a new module. But if the module does not exist in\n``sys.modules`` then it is to be added to that dict before\ninitialization begins. If an error occurs during loading of the module\nand it was added to ``sys.modules`` it is to be removed from the dict.\nIf an error occurs but the module was already in ``sys.modules`` it is\nleft in the dict.\n\nThe loader must set several attributes on the module. ``__name__`` is\nto be set to the name of the module. ``__file__`` is to be the "path"\nto the file unless the module is built-in (and thus listed in\n``sys.builtin_module_names``) in which case the attribute is not set.\nIf what is being imported is a package then ``__path__`` is to be set\nto a list of paths to be searched when looking for modules and\npackages contained within the package being imported. ``__package__``\nis optional but should be set to the name of package that contains the\nmodule or package (the empty string is used for module not contained\nin a package). ``__loader__`` is also optional but should be set to\nthe loader object that is loading the module.\n\nIf an error occurs during loading then the loader raises\n``ImportError`` if some other exception is not already being\npropagated. Otherwise the loader returns the module that was loaded\nand initialized.\n\nWhen step (1) finishes without raising an exception, step (2) can\nbegin.\n\nThe first form of ``import`` statement binds the module name in the\nlocal namespace to the module object, and then goes on to import the\nnext identifier, if any. If the module name is followed by ``as``,\nthe name following ``as`` is used as the local name for the module.\n\nThe ``from`` form does not bind the module name: it goes through the\nlist of identifiers, looks each one of them up in the module found in\nstep (1), and binds the name in the local namespace to the object thus\nfound. As with the first form of ``import``, an alternate local name\ncan be supplied by specifying "``as`` localname". If a name is not\nfound, ``ImportError`` is raised. If the list of identifiers is\nreplaced by a star (``\'*\'``), all public names defined in the module\nare bound in the local namespace of the ``import`` statement..\n\nThe *public names* defined by a module are determined by checking the\nmodule\'s namespace for a variable named ``__all__``; if defined, it\nmust be a sequence of strings which are names defined or imported by\nthat module. The names given in ``__all__`` are all considered public\nand are required to exist. If ``__all__`` is not defined, the set of\npublic names includes all names found in the module\'s namespace which\ndo not begin with an underscore character (``\'_\'``). ``__all__``\nshould contain the entire public API. It is intended to avoid\naccidentally exporting items that are not part of the API (such as\nlibrary modules which were imported and used within the module).\n\nThe ``from`` form with ``*`` may only occur in a module scope. If the\nwild card form of import --- ``import *`` --- is used in a function\nand the function contains or is a nested block with free variables,\nthe compiler will raise a ``SyntaxError``.\n\nWhen specifying what module to import you do not have to specify the\nabsolute name of the module. When a module or package is contained\nwithin another package it is possible to make a relative import within\nthe same top package without having to mention the package name. By\nusing leading dots in the specified module or package after ``from``\nyou can specify how high to traverse up the current package hierarchy\nwithout specifying exact names. One leading dot means the current\npackage where the module making the import exists. Two dots means up\none package level. Three dots is up two levels, etc. So if you execute\n``from . import mod`` from a module in the ``pkg`` package then you\nwill end up importing ``pkg.mod``. If you execute ``from ..subpkg2\nimprt mod`` from within ``pkg.subpkg1`` you will import\n``pkg.subpkg2.mod``. The specification for relative imports is\ncontained within **PEP 328**.\n\nThe built-in function ``__import__()`` is provided to support\napplications that determine which modules need to be loaded\ndynamically; refer to *Built-in Functions* for additional information.\n\n\nFuture statements\n=================\n\nA *future statement* is a directive to the compiler that a particular\nmodule should be compiled using syntax or semantics that will be\navailable in a specified future release of Python. The future\nstatement is intended to ease migration to future versions of Python\nthat introduce incompatible changes to the language. It allows use of\nthe new features on a per-module basis before the release in which the\nfeature becomes standard.\n\n future_statement ::= "from" "__future__" "import" feature ["as" name]\n ("," feature ["as" name])*\n | "from" "__future__" "import" "(" feature ["as" name]\n ("," feature ["as" name])* [","] ")"\n feature ::= identifier\n name ::= identifier\n\nA future statement must appear near the top of the module. The only\nlines that can appear before a future statement are:\n\n* the module docstring (if any),\n\n* comments,\n\n* blank lines, and\n\n* other future statements.\n\nThe features recognized by Python 2.6 are ``unicode_literals``,\n``print_function``, ``absolute_import``, ``division``, ``generators``,\n``nested_scopes`` and ``with_statement``. ``generators``,\n``with_statement``, ``nested_scopes`` are redundant in Python version\n2.6 and above because they are always enabled.\n\nA future statement is recognized and treated specially at compile\ntime: Changes to the semantics of core constructs are often\nimplemented by generating different code. It may even be the case\nthat a new feature introduces new incompatible syntax (such as a new\nreserved word), in which case the compiler may need to parse the\nmodule differently. Such decisions cannot be pushed off until\nruntime.\n\nFor any given release, the compiler knows which feature names have\nbeen defined, and raises a compile-time error if a future statement\ncontains a feature not known to it.\n\nThe direct runtime semantics are the same as for any import statement:\nthere is a standard module ``__future__``, described later, and it\nwill be imported in the usual way at the time the future statement is\nexecuted.\n\nThe interesting runtime semantics depend on the specific feature\nenabled by the future statement.\n\nNote that there is nothing special about the statement:\n\n import __future__ [as name]\n\nThat is not a future statement; it\'s an ordinary import statement with\nno special semantics or syntax restrictions.\n\nCode compiled by an ``exec`` statement or calls to the built-in\nfunctions ``compile()`` and ``execfile()`` that occur in a module\n``M`` containing a future statement will, by default, use the new\nsyntax or semantics associated with the future statement. This can,\nstarting with Python 2.2 be controlled by optional arguments to\n``compile()`` --- see the documentation of that function for details.\n\nA future statement typed at an interactive interpreter prompt will\ntake effect for the rest of the interpreter session. If an\ninterpreter is started with the *-i* option, is passed a script name\nto execute, and the script includes a future statement, it will be in\neffect in the interactive session started after the script is\nexecuted.\n\nSee also:\n\n **PEP 236** - Back to the __future__\n The original proposal for the __future__ mechanism.\n', 'in': u'\nComparisons\n***********\n\nUnlike C, all comparison operations in Python have the same priority,\nwhich is lower than that of any arithmetic, shifting or bitwise\noperation. Also unlike C, expressions like ``a < b < c`` have the\ninterpretation that is conventional in mathematics:\n\n comparison ::= or_expr ( comp_operator or_expr )*\n comp_operator ::= "<" | ">" | "==" | ">=" | "<=" | "<>" | "!="\n | "is" ["not"] | ["not"] "in"\n\nComparisons yield boolean values: ``True`` or ``False``.\n\nComparisons can be chained arbitrarily, e.g., ``x < y <= z`` is\nequivalent to ``x < y and y <= z``, except that ``y`` is evaluated\nonly once (but in both cases ``z`` is not evaluated at all when ``x <\ny`` is found to be false).\n\nFormally, if *a*, *b*, *c*, ..., *y*, *z* are expressions and *op1*,\n*op2*, ..., *opN* are comparison operators, then ``a op1 b op2 c ... y\nopN z`` is equivalent to ``a op1 b and b op2 c and ... y opN z``,\nexcept that each expression is evaluated at most once.\n\nNote that ``a op1 b op2 c`` doesn\'t imply any kind of comparison\nbetween *a* and *c*, so that, e.g., ``x < y > z`` is perfectly legal\n(though perhaps not pretty).\n\nThe forms ``<>`` and ``!=`` are equivalent; for consistency with C,\n``!=`` is preferred; where ``!=`` is mentioned below ``<>`` is also\naccepted. The ``<>`` spelling is considered obsolescent.\n\nThe operators ``<``, ``>``, ``==``, ``>=``, ``<=``, and ``!=`` compare\nthe values of two objects. The objects need not have the same type.\nIf both are numbers, they are converted to a common type. Otherwise,\nobjects of different types *always* compare unequal, and are ordered\nconsistently but arbitrarily. You can control comparison behavior of\nobjects of non-built-in types by defining a ``__cmp__`` method or rich\ncomparison methods like ``__gt__``, described in section *Special\nmethod names*.\n\n(This unusual definition of comparison was used to simplify the\ndefinition of operations like sorting and the ``in`` and ``not in``\noperators. In the future, the comparison rules for objects of\ndifferent types are likely to change.)\n\nComparison of objects of the same type depends on the type:\n\n* Numbers are compared arithmetically.\n\n* Strings are compared lexicographically using the numeric equivalents\n (the result of the built-in function ``ord()``) of their characters.\n Unicode and 8-bit strings are fully interoperable in this behavior.\n [4]\n\n* Tuples and lists are compared lexicographically using comparison of\n corresponding elements. This means that to compare equal, each\n element must compare equal and the two sequences must be of the same\n type and have the same length.\n\n If not equal, the sequences are ordered the same as their first\n differing elements. For example, ``cmp([1,2,x], [1,2,y])`` returns\n the same as ``cmp(x,y)``. If the corresponding element does not\n exist, the shorter sequence is ordered first (for example, ``[1,2] <\n [1,2,3]``).\n\n* Mappings (dictionaries) compare equal if and only if their sorted\n (key, value) lists compare equal. [5] Outcomes other than equality\n are resolved consistently, but are not otherwise defined. [6]\n\n* Most other objects of built-in types compare unequal unless they are\n the same object; the choice whether one object is considered smaller\n or larger than another one is made arbitrarily but consistently\n within one execution of a program.\n\nThe operators ``in`` and ``not in`` test for collection membership.\n``x in s`` evaluates to true if *x* is a member of the collection *s*,\nand false otherwise. ``x not in s`` returns the negation of ``x in\ns``. The collection membership test has traditionally been bound to\nsequences; an object is a member of a collection if the collection is\na sequence and contains an element equal to that object. However, it\nmake sense for many other object types to support membership tests\nwithout being a sequence. In particular, dictionaries (for keys) and\nsets support membership testing.\n\nFor the list and tuple types, ``x in y`` is true if and only if there\nexists an index *i* such that ``x == y[i]`` is true.\n\nFor the Unicode and string types, ``x in y`` is true if and only if\n*x* is a substring of *y*. An equivalent test is ``y.find(x) != -1``.\nNote, *x* and *y* need not be the same type; consequently, ``u\'ab\' in\n\'abc\'`` will return ``True``. Empty strings are always considered to\nbe a substring of any other string, so ``"" in "abc"`` will return\n``True``.\n\nChanged in version 2.3: Previously, *x* was required to be a string of\nlength ``1``.\n\nFor user-defined classes which define the ``__contains__()`` method,\n``x in y`` is true if and only if ``y.__contains__(x)`` is true.\n\nFor user-defined classes which do not define ``__contains__()`` but do\ndefine ``__iter__()``, ``x in y`` is true if some value ``z`` with ``x\n== z`` is produced while iterating over ``y``. If an exception is\nraised during the iteration, it is as if ``in`` raised that exception.\n\nLastly, the old-style iteration protocol is tried: if a class defines\n``__getitem__()``, ``x in y`` is true if and only if there is a non-\nnegative integer index *i* such that ``x == y[i]``, and all lower\ninteger indices do not raise ``IndexError`` exception. (If any other\nexception is raised, it is as if ``in`` raised that exception).\n\nThe operator ``not in`` is defined to have the inverse true value of\n``in``.\n\nThe operators ``is`` and ``is not`` test for object identity: ``x is\ny`` is true if and only if *x* and *y* are the same object. ``x is\nnot y`` yields the inverse truth value. [7]\n', 'integers': u'\nInteger and long integer literals\n*********************************\n\nInteger and long integer literals are described by the following\nlexical definitions:\n\n longinteger ::= integer ("l" | "L")\n integer ::= decimalinteger | octinteger | hexinteger | bininteger\n decimalinteger ::= nonzerodigit digit* | "0"\n octinteger ::= "0" ("o" | "O") octdigit+ | "0" octdigit+\n hexinteger ::= "0" ("x" | "X") hexdigit+\n bininteger ::= "0" ("b" | "B") bindigit+\n nonzerodigit ::= "1"..."9"\n octdigit ::= "0"..."7"\n bindigit ::= "0" | "1"\n hexdigit ::= digit | "a"..."f" | "A"..."F"\n\nAlthough both lower case ``\'l\'`` and upper case ``\'L\'`` are allowed as\nsuffix for long integers, it is strongly recommended to always use\n``\'L\'``, since the letter ``\'l\'`` looks too much like the digit\n``\'1\'``.\n\nPlain integer literals that are above the largest representable plain\ninteger (e.g., 2147483647 when using 32-bit arithmetic) are accepted\nas if they were long integers instead. [1] There is no limit for long\ninteger literals apart from what can be stored in available memory.\n\nSome examples of plain integer literals (first row) and long integer\nliterals (second and third rows):\n\n 7 2147483647 0177\n 3L 79228162514264337593543950336L 0377L 0x100000000L\n 79228162514264337593543950336 0xdeadbeef\n', 'lambda': u'\nLambdas\n*******\n\n lambda_form ::= "lambda" [parameter_list]: expression\n old_lambda_form ::= "lambda" [parameter_list]: old_expression\n\nLambda forms (lambda expressions) have the same syntactic position as\nexpressions. They are a shorthand to create anonymous functions; the\nexpression ``lambda arguments: expression`` yields a function object.\nThe unnamed object behaves like a function object defined with\n\n def name(arguments):\n return expression\n\nSee section *Function definitions* for the syntax of parameter lists.\nNote that functions created with lambda forms cannot contain\nstatements.\n', - 'lists': u'\nList displays\n*************\n\nA list display is a possibly empty series of expressions enclosed in\nsquare brackets:\n\n list_display ::= "[" [expression_list | list_comprehension] "]"\n list_comprehension ::= expression list_for\n list_for ::= "for" target_list "in" old_expression_list [list_iter]\n old_expression_list ::= old_expression [("," old_expression)+ [","]]\n list_iter ::= list_for | list_if\n list_if ::= "if" old_expression [list_iter]\n\nA list display yields a new list object. Its contents are specified\nby providing either a list of expressions or a list comprehension.\nWhen a comma-separated list of expressions is supplied, its elements\nare evaluated from left to right and placed into the list object in\nthat order. When a list comprehension is supplied, it consists of a\nsingle expression followed by at least one ``for`` clause and zero or\nmore ``for`` or ``if`` clauses. In this case, the elements of the new\nlist are those that would be produced by considering each of the\n``for`` or ``if`` clauses a block, nesting from left to right, and\nevaluating the expression to produce a list element each time the\ninnermost block is reached [1].\n', - 'naming': u"\nNaming and binding\n******************\n\n*Names* refer to objects. Names are introduced by name binding\noperations. Each occurrence of a name in the program text refers to\nthe *binding* of that name established in the innermost function block\ncontaining the use.\n\nA *block* is a piece of Python program text that is executed as a\nunit. The following are blocks: a module, a function body, and a class\ndefinition. Each command typed interactively is a block. A script\nfile (a file given as standard input to the interpreter or specified\non the interpreter command line the first argument) is a code block.\nA script command (a command specified on the interpreter command line\nwith the '**-c**' option) is a code block. The file read by the\nbuilt-in function ``execfile()`` is a code block. The string argument\npassed to the built-in function ``eval()`` and to the ``exec``\nstatement is a code block. The expression read and evaluated by the\nbuilt-in function ``input()`` is a code block.\n\nA code block is executed in an *execution frame*. A frame contains\nsome administrative information (used for debugging) and determines\nwhere and how execution continues after the code block's execution has\ncompleted.\n\nA *scope* defines the visibility of a name within a block. If a local\nvariable is defined in a block, its scope includes that block. If the\ndefinition occurs in a function block, the scope extends to any blocks\ncontained within the defining one, unless a contained block introduces\na different binding for the name. The scope of names defined in a\nclass block is limited to the class block; it does not extend to the\ncode blocks of methods -- this includes generator expressions since\nthey are implemented using a function scope. This means that the\nfollowing will fail:\n\n class A:\n a = 42\n b = list(a + i for i in range(10))\n\nWhen a name is used in a code block, it is resolved using the nearest\nenclosing scope. The set of all such scopes visible to a code block\nis called the block's *environment*.\n\nIf a name is bound in a block, it is a local variable of that block.\nIf a name is bound at the module level, it is a global variable. (The\nvariables of the module code block are local and global.) If a\nvariable is used in a code block but not defined there, it is a *free\nvariable*.\n\nWhen a name is not found at all, a ``NameError`` exception is raised.\nIf the name refers to a local variable that has not been bound, a\n``UnboundLocalError`` exception is raised. ``UnboundLocalError`` is a\nsubclass of ``NameError``.\n\nThe following constructs bind names: formal parameters to functions,\n``import`` statements, class and function definitions (these bind the\nclass or function name in the defining block), and targets that are\nidentifiers if occurring in an assignment, ``for`` loop header, in the\nsecond position of an ``except`` clause header or after ``as`` in a\n``with`` statement. The ``import`` statement of the form ``from ...\nimport *`` binds all names defined in the imported module, except\nthose beginning with an underscore. This form may only be used at the\nmodule level.\n\nA target occurring in a ``del`` statement is also considered bound for\nthis purpose (though the actual semantics are to unbind the name). It\nis illegal to unbind a name that is referenced by an enclosing scope;\nthe compiler will report a ``SyntaxError``.\n\nEach assignment or import statement occurs within a block defined by a\nclass or function definition or at the module level (the top-level\ncode block).\n\nIf a name binding operation occurs anywhere within a code block, all\nuses of the name within the block are treated as references to the\ncurrent block. This can lead to errors when a name is used within a\nblock before it is bound. This rule is subtle. Python lacks\ndeclarations and allows name binding operations to occur anywhere\nwithin a code block. The local variables of a code block can be\ndetermined by scanning the entire text of the block for name binding\noperations.\n\nIf the global statement occurs within a block, all uses of the name\nspecified in the statement refer to the binding of that name in the\ntop-level namespace. Names are resolved in the top-level namespace by\nsearching the global namespace, i.e. the namespace of the module\ncontaining the code block, and the builtins namespace, the namespace\nof the module ``__builtin__``. The global namespace is searched\nfirst. If the name is not found there, the builtins namespace is\nsearched. The global statement must precede all uses of the name.\n\nThe built-in namespace associated with the execution of a code block\nis actually found by looking up the name ``__builtins__`` in its\nglobal namespace; this should be a dictionary or a module (in the\nlatter case the module's dictionary is used). By default, when in the\n``__main__`` module, ``__builtins__`` is the built-in module\n``__builtin__`` (note: no 's'); when in any other module,\n``__builtins__`` is an alias for the dictionary of the ``__builtin__``\nmodule itself. ``__builtins__`` can be set to a user-created\ndictionary to create a weak form of restricted execution.\n\n**CPython implementation detail:** Users should not touch\n``__builtins__``; it is strictly an implementation detail. Users\nwanting to override values in the built-in namespace should ``import``\nthe ``__builtin__`` (no 's') module and modify its attributes\nappropriately.\n\nThe namespace for a module is automatically created the first time a\nmodule is imported. The main module for a script is always called\n``__main__``.\n\nThe global statement has the same scope as a name binding operation in\nthe same block. If the nearest enclosing scope for a free variable\ncontains a global statement, the free variable is treated as a global.\n\nA class definition is an executable statement that may use and define\nnames. These references follow the normal rules for name resolution.\nThe namespace of the class definition becomes the attribute dictionary\nof the class. Names defined at the class scope are not visible in\nmethods.\n\n\nInteraction with dynamic features\n=================================\n\nThere are several cases where Python statements are illegal when used\nin conjunction with nested scopes that contain free variables.\n\nIf a variable is referenced in an enclosing scope, it is illegal to\ndelete the name. An error will be reported at compile time.\n\nIf the wild card form of import --- ``import *`` --- is used in a\nfunction and the function contains or is a nested block with free\nvariables, the compiler will raise a ``SyntaxError``.\n\nIf ``exec`` is used in a function and the function contains or is a\nnested block with free variables, the compiler will raise a\n``SyntaxError`` unless the exec explicitly specifies the local\nnamespace for the ``exec``. (In other words, ``exec obj`` would be\nillegal, but ``exec obj in ns`` would be legal.)\n\nThe ``eval()``, ``execfile()``, and ``input()`` functions and the\n``exec`` statement do not have access to the full environment for\nresolving names. Names may be resolved in the local and global\nnamespaces of the caller. Free variables are not resolved in the\nnearest enclosing namespace, but in the global namespace. [1] The\n``exec`` statement and the ``eval()`` and ``execfile()`` functions\nhave optional arguments to override the global and local namespace.\nIf only one namespace is specified, it is used for both.\n", + 'lists': u'\nList displays\n*************\n\nA list display is a possibly empty series of expressions enclosed in\nsquare brackets:\n\n list_display ::= "[" [expression_list | list_comprehension] "]"\n list_comprehension ::= expression list_for\n list_for ::= "for" target_list "in" old_expression_list [list_iter]\n old_expression_list ::= old_expression [("," old_expression)+ [","]]\n old_expression ::= or_test | old_lambda_form\n list_iter ::= list_for | list_if\n list_if ::= "if" old_expression [list_iter]\n\nA list display yields a new list object. Its contents are specified\nby providing either a list of expressions or a list comprehension.\nWhen a comma-separated list of expressions is supplied, its elements\nare evaluated from left to right and placed into the list object in\nthat order. When a list comprehension is supplied, it consists of a\nsingle expression followed by at least one ``for`` clause and zero or\nmore ``for`` or ``if`` clauses. In this case, the elements of the new\nlist are those that would be produced by considering each of the\n``for`` or ``if`` clauses a block, nesting from left to right, and\nevaluating the expression to produce a list element each time the\ninnermost block is reached [1].\n', + 'naming': u"\nNaming and binding\n******************\n\n*Names* refer to objects. Names are introduced by name binding\noperations. Each occurrence of a name in the program text refers to\nthe *binding* of that name established in the innermost function block\ncontaining the use.\n\nA *block* is a piece of Python program text that is executed as a\nunit. The following are blocks: a module, a function body, and a class\ndefinition. Each command typed interactively is a block. A script\nfile (a file given as standard input to the interpreter or specified\non the interpreter command line the first argument) is a code block.\nA script command (a command specified on the interpreter command line\nwith the '**-c**' option) is a code block. The file read by the\nbuilt-in function ``execfile()`` is a code block. The string argument\npassed to the built-in function ``eval()`` and to the ``exec``\nstatement is a code block. The expression read and evaluated by the\nbuilt-in function ``input()`` is a code block.\n\nA code block is executed in an *execution frame*. A frame contains\nsome administrative information (used for debugging) and determines\nwhere and how execution continues after the code block's execution has\ncompleted.\n\nA *scope* defines the visibility of a name within a block. If a local\nvariable is defined in a block, its scope includes that block. If the\ndefinition occurs in a function block, the scope extends to any blocks\ncontained within the defining one, unless a contained block introduces\na different binding for the name. The scope of names defined in a\nclass block is limited to the class block; it does not extend to the\ncode blocks of methods -- this includes generator expressions since\nthey are implemented using a function scope. This means that the\nfollowing will fail:\n\n class A:\n a = 42\n b = list(a + i for i in range(10))\n\nWhen a name is used in a code block, it is resolved using the nearest\nenclosing scope. The set of all such scopes visible to a code block\nis called the block's *environment*.\n\nIf a name is bound in a block, it is a local variable of that block.\nIf a name is bound at the module level, it is a global variable. (The\nvariables of the module code block are local and global.) If a\nvariable is used in a code block but not defined there, it is a *free\nvariable*.\n\nWhen a name is not found at all, a ``NameError`` exception is raised.\nIf the name refers to a local variable that has not been bound, a\n``UnboundLocalError`` exception is raised. ``UnboundLocalError`` is a\nsubclass of ``NameError``.\n\nThe following constructs bind names: formal parameters to functions,\n``import`` statements, class and function definitions (these bind the\nclass or function name in the defining block), and targets that are\nidentifiers if occurring in an assignment, ``for`` loop header, in the\nsecond position of an ``except`` clause header or after ``as`` in a\n``with`` statement. The ``import`` statement of the form ``from ...\nimport *`` binds all names defined in the imported module, except\nthose beginning with an underscore. This form may only be used at the\nmodule level.\n\nA target occurring in a ``del`` statement is also considered bound for\nthis purpose (though the actual semantics are to unbind the name). It\nis illegal to unbind a name that is referenced by an enclosing scope;\nthe compiler will report a ``SyntaxError``.\n\nEach assignment or import statement occurs within a block defined by a\nclass or function definition or at the module level (the top-level\ncode block).\n\nIf a name binding operation occurs anywhere within a code block, all\nuses of the name within the block are treated as references to the\ncurrent block. This can lead to errors when a name is used within a\nblock before it is bound. This rule is subtle. Python lacks\ndeclarations and allows name binding operations to occur anywhere\nwithin a code block. The local variables of a code block can be\ndetermined by scanning the entire text of the block for name binding\noperations.\n\nIf the global statement occurs within a block, all uses of the name\nspecified in the statement refer to the binding of that name in the\ntop-level namespace. Names are resolved in the top-level namespace by\nsearching the global namespace, i.e. the namespace of the module\ncontaining the code block, and the builtins namespace, the namespace\nof the module ``__builtin__``. The global namespace is searched\nfirst. If the name is not found there, the builtins namespace is\nsearched. The global statement must precede all uses of the name.\n\nThe builtins namespace associated with the execution of a code block\nis actually found by looking up the name ``__builtins__`` in its\nglobal namespace; this should be a dictionary or a module (in the\nlatter case the module's dictionary is used). By default, when in the\n``__main__`` module, ``__builtins__`` is the built-in module\n``__builtin__`` (note: no 's'); when in any other module,\n``__builtins__`` is an alias for the dictionary of the ``__builtin__``\nmodule itself. ``__builtins__`` can be set to a user-created\ndictionary to create a weak form of restricted execution.\n\n**CPython implementation detail:** Users should not touch\n``__builtins__``; it is strictly an implementation detail. Users\nwanting to override values in the builtins namespace should ``import``\nthe ``__builtin__`` (no 's') module and modify its attributes\nappropriately.\n\nThe namespace for a module is automatically created the first time a\nmodule is imported. The main module for a script is always called\n``__main__``.\n\nThe global statement has the same scope as a name binding operation in\nthe same block. If the nearest enclosing scope for a free variable\ncontains a global statement, the free variable is treated as a global.\n\nA class definition is an executable statement that may use and define\nnames. These references follow the normal rules for name resolution.\nThe namespace of the class definition becomes the attribute dictionary\nof the class. Names defined at the class scope are not visible in\nmethods.\n\n\nInteraction with dynamic features\n=================================\n\nThere are several cases where Python statements are illegal when used\nin conjunction with nested scopes that contain free variables.\n\nIf a variable is referenced in an enclosing scope, it is illegal to\ndelete the name. An error will be reported at compile time.\n\nIf the wild card form of import --- ``import *`` --- is used in a\nfunction and the function contains or is a nested block with free\nvariables, the compiler will raise a ``SyntaxError``.\n\nIf ``exec`` is used in a function and the function contains or is a\nnested block with free variables, the compiler will raise a\n``SyntaxError`` unless the exec explicitly specifies the local\nnamespace for the ``exec``. (In other words, ``exec obj`` would be\nillegal, but ``exec obj in ns`` would be legal.)\n\nThe ``eval()``, ``execfile()``, and ``input()`` functions and the\n``exec`` statement do not have access to the full environment for\nresolving names. Names may be resolved in the local and global\nnamespaces of the caller. Free variables are not resolved in the\nnearest enclosing namespace, but in the global namespace. [1] The\n``exec`` statement and the ``eval()`` and ``execfile()`` functions\nhave optional arguments to override the global and local namespace.\nIf only one namespace is specified, it is used for both.\n", 'numbers': u"\nNumeric literals\n****************\n\nThere are four types of numeric literals: plain integers, long\nintegers, floating point numbers, and imaginary numbers. There are no\ncomplex literals (complex numbers can be formed by adding a real\nnumber and an imaginary number).\n\nNote that numeric literals do not include a sign; a phrase like ``-1``\nis actually an expression composed of the unary operator '``-``' and\nthe literal ``1``.\n", - 'numeric-types': u'\nEmulating numeric types\n***********************\n\nThe following methods can be defined to emulate numeric objects.\nMethods corresponding to operations that are not supported by the\nparticular kind of number implemented (e.g., bitwise operations for\nnon-integral numbers) should be left undefined.\n\nobject.__add__(self, other)\nobject.__sub__(self, other)\nobject.__mul__(self, other)\nobject.__floordiv__(self, other)\nobject.__mod__(self, other)\nobject.__divmod__(self, other)\nobject.__pow__(self, other[, modulo])\nobject.__lshift__(self, other)\nobject.__rshift__(self, other)\nobject.__and__(self, other)\nobject.__xor__(self, other)\nobject.__or__(self, other)\n\n These methods are called to implement the binary arithmetic\n operations (``+``, ``-``, ``*``, ``//``, ``%``, ``divmod()``,\n ``pow()``, ``**``, ``<<``, ``>>``, ``&``, ``^``, ``|``). For\n instance, to evaluate the expression ``x + y``, where *x* is an\n instance of a class that has an ``__add__()`` method,\n ``x.__add__(y)`` is called. The ``__divmod__()`` method should be\n the equivalent to using ``__floordiv__()`` and ``__mod__()``; it\n should not be related to ``__truediv__()`` (described below). Note\n that ``__pow__()`` should be defined to accept an optional third\n argument if the ternary version of the built-in ``pow()`` function\n is to be supported.\n\n If one of those methods does not support the operation with the\n supplied arguments, it should return ``NotImplemented``.\n\nobject.__div__(self, other)\nobject.__truediv__(self, other)\n\n The division operator (``/``) is implemented by these methods. The\n ``__truediv__()`` method is used when ``__future__.division`` is in\n effect, otherwise ``__div__()`` is used. If only one of these two\n methods is defined, the object will not support division in the\n alternate context; ``TypeError`` will be raised instead.\n\nobject.__radd__(self, other)\nobject.__rsub__(self, other)\nobject.__rmul__(self, other)\nobject.__rdiv__(self, other)\nobject.__rtruediv__(self, other)\nobject.__rfloordiv__(self, other)\nobject.__rmod__(self, other)\nobject.__rdivmod__(self, other)\nobject.__rpow__(self, other)\nobject.__rlshift__(self, other)\nobject.__rrshift__(self, other)\nobject.__rand__(self, other)\nobject.__rxor__(self, other)\nobject.__ror__(self, other)\n\n These methods are called to implement the binary arithmetic\n operations (``+``, ``-``, ``*``, ``/``, ``%``, ``divmod()``,\n ``pow()``, ``**``, ``<<``, ``>>``, ``&``, ``^``, ``|``) with\n reflected (swapped) operands. These functions are only called if\n the left operand does not support the corresponding operation and\n the operands are of different types. [3] For instance, to evaluate\n the expression ``x - y``, where *y* is an instance of a class that\n has an ``__rsub__()`` method, ``y.__rsub__(x)`` is called if\n ``x.__sub__(y)`` returns *NotImplemented*.\n\n Note that ternary ``pow()`` will not try calling ``__rpow__()``\n (the coercion rules would become too complicated).\n\n Note: If the right operand\'s type is a subclass of the left operand\'s\n type and that subclass provides the reflected method for the\n operation, this method will be called before the left operand\'s\n non-reflected method. This behavior allows subclasses to\n override their ancestors\' operations.\n\nobject.__iadd__(self, other)\nobject.__isub__(self, other)\nobject.__imul__(self, other)\nobject.__idiv__(self, other)\nobject.__itruediv__(self, other)\nobject.__ifloordiv__(self, other)\nobject.__imod__(self, other)\nobject.__ipow__(self, other[, modulo])\nobject.__ilshift__(self, other)\nobject.__irshift__(self, other)\nobject.__iand__(self, other)\nobject.__ixor__(self, other)\nobject.__ior__(self, other)\n\n These methods are called to implement the augmented arithmetic\n assignments (``+=``, ``-=``, ``*=``, ``/=``, ``//=``, ``%=``,\n ``**=``, ``<<=``, ``>>=``, ``&=``, ``^=``, ``|=``). These methods\n should attempt to do the operation in-place (modifying *self*) and\n return the result (which could be, but does not have to be,\n *self*). If a specific method is not defined, the augmented\n assignment falls back to the normal methods. For instance, to\n execute the statement ``x += y``, where *x* is an instance of a\n class that has an ``__iadd__()`` method, ``x.__iadd__(y)`` is\n called. If *x* is an instance of a class that does not define a\n ``__iadd__()`` method, ``x.__add__(y)`` and ``y.__radd__(x)`` are\n considered, as with the evaluation of ``x + y``.\n\nobject.__neg__(self)\nobject.__pos__(self)\nobject.__abs__(self)\nobject.__invert__(self)\n\n Called to implement the unary arithmetic operations (``-``, ``+``,\n ``abs()`` and ``~``).\n\nobject.__complex__(self)\nobject.__int__(self)\nobject.__long__(self)\nobject.__float__(self)\n\n Called to implement the built-in functions ``complex()``,\n ``int()``, ``long()``, and ``float()``. Should return a value of\n the appropriate type.\n\nobject.__oct__(self)\nobject.__hex__(self)\n\n Called to implement the built-in functions ``oct()`` and ``hex()``.\n Should return a string value.\n\nobject.__index__(self)\n\n Called to implement ``operator.index()``. Also called whenever\n Python needs an integer object (such as in slicing). Must return\n an integer (int or long).\n\n New in version 2.5.\n\nobject.__coerce__(self, other)\n\n Called to implement "mixed-mode" numeric arithmetic. Should either\n return a 2-tuple containing *self* and *other* converted to a\n common numeric type, or ``None`` if conversion is impossible. When\n the common type would be the type of ``other``, it is sufficient to\n return ``None``, since the interpreter will also ask the other\n object to attempt a coercion (but sometimes, if the implementation\n of the other type cannot be changed, it is useful to do the\n conversion to the other type here). A return value of\n ``NotImplemented`` is equivalent to returning ``None``.\n', + 'numeric-types': u'\nEmulating numeric types\n***********************\n\nThe following methods can be defined to emulate numeric objects.\nMethods corresponding to operations that are not supported by the\nparticular kind of number implemented (e.g., bitwise operations for\nnon-integral numbers) should be left undefined.\n\nobject.__add__(self, other)\nobject.__sub__(self, other)\nobject.__mul__(self, other)\nobject.__floordiv__(self, other)\nobject.__mod__(self, other)\nobject.__divmod__(self, other)\nobject.__pow__(self, other[, modulo])\nobject.__lshift__(self, other)\nobject.__rshift__(self, other)\nobject.__and__(self, other)\nobject.__xor__(self, other)\nobject.__or__(self, other)\n\n These methods are called to implement the binary arithmetic\n operations (``+``, ``-``, ``*``, ``//``, ``%``, ``divmod()``,\n ``pow()``, ``**``, ``<<``, ``>>``, ``&``, ``^``, ``|``). For\n instance, to evaluate the expression ``x + y``, where *x* is an\n instance of a class that has an ``__add__()`` method,\n ``x.__add__(y)`` is called. The ``__divmod__()`` method should be\n the equivalent to using ``__floordiv__()`` and ``__mod__()``; it\n should not be related to ``__truediv__()`` (described below). Note\n that ``__pow__()`` should be defined to accept an optional third\n argument if the ternary version of the built-in ``pow()`` function\n is to be supported.\n\n If one of those methods does not support the operation with the\n supplied arguments, it should return ``NotImplemented``.\n\nobject.__div__(self, other)\nobject.__truediv__(self, other)\n\n The division operator (``/``) is implemented by these methods. The\n ``__truediv__()`` method is used when ``__future__.division`` is in\n effect, otherwise ``__div__()`` is used. If only one of these two\n methods is defined, the object will not support division in the\n alternate context; ``TypeError`` will be raised instead.\n\nobject.__radd__(self, other)\nobject.__rsub__(self, other)\nobject.__rmul__(self, other)\nobject.__rdiv__(self, other)\nobject.__rtruediv__(self, other)\nobject.__rfloordiv__(self, other)\nobject.__rmod__(self, other)\nobject.__rdivmod__(self, other)\nobject.__rpow__(self, other)\nobject.__rlshift__(self, other)\nobject.__rrshift__(self, other)\nobject.__rand__(self, other)\nobject.__rxor__(self, other)\nobject.__ror__(self, other)\n\n These methods are called to implement the binary arithmetic\n operations (``+``, ``-``, ``*``, ``/``, ``%``, ``divmod()``,\n ``pow()``, ``**``, ``<<``, ``>>``, ``&``, ``^``, ``|``) with\n reflected (swapped) operands. These functions are only called if\n the left operand does not support the corresponding operation and\n the operands are of different types. [2] For instance, to evaluate\n the expression ``x - y``, where *y* is an instance of a class that\n has an ``__rsub__()`` method, ``y.__rsub__(x)`` is called if\n ``x.__sub__(y)`` returns *NotImplemented*.\n\n Note that ternary ``pow()`` will not try calling ``__rpow__()``\n (the coercion rules would become too complicated).\n\n Note: If the right operand\'s type is a subclass of the left operand\'s\n type and that subclass provides the reflected method for the\n operation, this method will be called before the left operand\'s\n non-reflected method. This behavior allows subclasses to\n override their ancestors\' operations.\n\nobject.__iadd__(self, other)\nobject.__isub__(self, other)\nobject.__imul__(self, other)\nobject.__idiv__(self, other)\nobject.__itruediv__(self, other)\nobject.__ifloordiv__(self, other)\nobject.__imod__(self, other)\nobject.__ipow__(self, other[, modulo])\nobject.__ilshift__(self, other)\nobject.__irshift__(self, other)\nobject.__iand__(self, other)\nobject.__ixor__(self, other)\nobject.__ior__(self, other)\n\n These methods are called to implement the augmented arithmetic\n assignments (``+=``, ``-=``, ``*=``, ``/=``, ``//=``, ``%=``,\n ``**=``, ``<<=``, ``>>=``, ``&=``, ``^=``, ``|=``). These methods\n should attempt to do the operation in-place (modifying *self*) and\n return the result (which could be, but does not have to be,\n *self*). If a specific method is not defined, the augmented\n assignment falls back to the normal methods. For instance, to\n execute the statement ``x += y``, where *x* is an instance of a\n class that has an ``__iadd__()`` method, ``x.__iadd__(y)`` is\n called. If *x* is an instance of a class that does not define a\n ``__iadd__()`` method, ``x.__add__(y)`` and ``y.__radd__(x)`` are\n considered, as with the evaluation of ``x + y``.\n\nobject.__neg__(self)\nobject.__pos__(self)\nobject.__abs__(self)\nobject.__invert__(self)\n\n Called to implement the unary arithmetic operations (``-``, ``+``,\n ``abs()`` and ``~``).\n\nobject.__complex__(self)\nobject.__int__(self)\nobject.__long__(self)\nobject.__float__(self)\n\n Called to implement the built-in functions ``complex()``,\n ``int()``, ``long()``, and ``float()``. Should return a value of\n the appropriate type.\n\nobject.__oct__(self)\nobject.__hex__(self)\n\n Called to implement the built-in functions ``oct()`` and ``hex()``.\n Should return a string value.\n\nobject.__index__(self)\n\n Called to implement ``operator.index()``. Also called whenever\n Python needs an integer object (such as in slicing). Must return\n an integer (int or long).\n\n New in version 2.5.\n\nobject.__coerce__(self, other)\n\n Called to implement "mixed-mode" numeric arithmetic. Should either\n return a 2-tuple containing *self* and *other* converted to a\n common numeric type, or ``None`` if conversion is impossible. When\n the common type would be the type of ``other``, it is sufficient to\n return ``None``, since the interpreter will also ask the other\n object to attempt a coercion (but sometimes, if the implementation\n of the other type cannot be changed, it is useful to do the\n conversion to the other type here). A return value of\n ``NotImplemented`` is equivalent to returning ``None``.\n', 'objects': u'\nObjects, values and types\n*************************\n\n*Objects* are Python\'s abstraction for data. All data in a Python\nprogram is represented by objects or by relations between objects. (In\na sense, and in conformance to Von Neumann\'s model of a "stored\nprogram computer," code is also represented by objects.)\n\nEvery object has an identity, a type and a value. An object\'s\n*identity* never changes once it has been created; you may think of it\nas the object\'s address in memory. The \'``is``\' operator compares the\nidentity of two objects; the ``id()`` function returns an integer\nrepresenting its identity (currently implemented as its address). An\nobject\'s *type* is also unchangeable. [1] An object\'s type determines\nthe operations that the object supports (e.g., "does it have a\nlength?") and also defines the possible values for objects of that\ntype. The ``type()`` function returns an object\'s type (which is an\nobject itself). The *value* of some objects can change. Objects\nwhose value can change are said to be *mutable*; objects whose value\nis unchangeable once they are created are called *immutable*. (The\nvalue of an immutable container object that contains a reference to a\nmutable object can change when the latter\'s value is changed; however\nthe container is still considered immutable, because the collection of\nobjects it contains cannot be changed. So, immutability is not\nstrictly the same as having an unchangeable value, it is more subtle.)\nAn object\'s mutability is determined by its type; for instance,\nnumbers, strings and tuples are immutable, while dictionaries and\nlists are mutable.\n\nObjects are never explicitly destroyed; however, when they become\nunreachable they may be garbage-collected. An implementation is\nallowed to postpone garbage collection or omit it altogether --- it is\na matter of implementation quality how garbage collection is\nimplemented, as long as no objects are collected that are still\nreachable.\n\n**CPython implementation detail:** CPython currently uses a reference-\ncounting scheme with (optional) delayed detection of cyclically linked\ngarbage, which collects most objects as soon as they become\nunreachable, but is not guaranteed to collect garbage containing\ncircular references. See the documentation of the ``gc`` module for\ninformation on controlling the collection of cyclic garbage. Other\nimplementations act differently and CPython may change.\n\nNote that the use of the implementation\'s tracing or debugging\nfacilities may keep objects alive that would normally be collectable.\nAlso note that catching an exception with a \'``try``...``except``\'\nstatement may keep objects alive.\n\nSome objects contain references to "external" resources such as open\nfiles or windows. It is understood that these resources are freed\nwhen the object is garbage-collected, but since garbage collection is\nnot guaranteed to happen, such objects also provide an explicit way to\nrelease the external resource, usually a ``close()`` method. Programs\nare strongly recommended to explicitly close such objects. The\n\'``try``...``finally``\' statement provides a convenient way to do\nthis.\n\nSome objects contain references to other objects; these are called\n*containers*. Examples of containers are tuples, lists and\ndictionaries. The references are part of a container\'s value. In\nmost cases, when we talk about the value of a container, we imply the\nvalues, not the identities of the contained objects; however, when we\ntalk about the mutability of a container, only the identities of the\nimmediately contained objects are implied. So, if an immutable\ncontainer (like a tuple) contains a reference to a mutable object, its\nvalue changes if that mutable object is changed.\n\nTypes affect almost all aspects of object behavior. Even the\nimportance of object identity is affected in some sense: for immutable\ntypes, operations that compute new values may actually return a\nreference to any existing object with the same type and value, while\nfor mutable objects this is not allowed. E.g., after ``a = 1; b =\n1``, ``a`` and ``b`` may or may not refer to the same object with the\nvalue one, depending on the implementation, but after ``c = []; d =\n[]``, ``c`` and ``d`` are guaranteed to refer to two different,\nunique, newly created empty lists. (Note that ``c = d = []`` assigns\nthe same object to both ``c`` and ``d``.)\n', - 'operator-summary': u'\nSummary\n*******\n\nThe following table summarizes the operator precedences in Python,\nfrom lowest precedence (least binding) to highest precedence (most\nbinding). Operators in the same box have the same precedence. Unless\nthe syntax is explicitly given, operators are binary. Operators in\nthe same box group left to right (except for comparisons, including\ntests, which all have the same precedence and chain from left to right\n--- see section *Comparisons* --- and exponentiation, which groups\nfrom right to left).\n\n+-------------------------------------------------+---------------------------------------+\n| Operator | Description |\n+=================================================+=======================================+\n| ``lambda`` | Lambda expression |\n+-------------------------------------------------+---------------------------------------+\n| ``or`` | Boolean OR |\n+-------------------------------------------------+---------------------------------------+\n| ``and`` | Boolean AND |\n+-------------------------------------------------+---------------------------------------+\n| ``not`` *x* | Boolean NOT |\n+-------------------------------------------------+---------------------------------------+\n| ``in``, ``not`` ``in``, ``is``, ``is not``, | Comparisons, including membership |\n| ``<``, ``<=``, ``>``, ``>=``, ``<>``, ``!=``, | tests and identity tests, |\n| ``==`` | |\n+-------------------------------------------------+---------------------------------------+\n| ``|`` | Bitwise OR |\n+-------------------------------------------------+---------------------------------------+\n| ``^`` | Bitwise XOR |\n+-------------------------------------------------+---------------------------------------+\n| ``&`` | Bitwise AND |\n+-------------------------------------------------+---------------------------------------+\n| ``<<``, ``>>`` | Shifts |\n+-------------------------------------------------+---------------------------------------+\n| ``+``, ``-`` | Addition and subtraction |\n+-------------------------------------------------+---------------------------------------+\n| ``*``, ``/``, ``//``, ``%`` | Multiplication, division, remainder |\n+-------------------------------------------------+---------------------------------------+\n| ``+x``, ``-x``, ``~x`` | Positive, negative, bitwise NOT |\n+-------------------------------------------------+---------------------------------------+\n| ``**`` | Exponentiation [8] |\n+-------------------------------------------------+---------------------------------------+\n| ``x[index]``, ``x[index:index]``, | Subscription, slicing, call, |\n| ``x(arguments...)``, ``x.attribute`` | attribute reference |\n+-------------------------------------------------+---------------------------------------+\n| ``(expressions...)``, ``[expressions...]``, | Binding or tuple display, list |\n| ``{key:datum...}``, ```expressions...``` | display, dictionary display, string |\n| | conversion |\n+-------------------------------------------------+---------------------------------------+\n\n-[ Footnotes ]-\n\n[1] In Python 2.3 and later releases, a list comprehension "leaks" the\n control variables of each ``for`` it contains into the containing\n scope. However, this behavior is deprecated, and relying on it\n will not work in Python 3.0\n\n[2] While ``abs(x%y) < abs(y)`` is true mathematically, for floats it\n may not be true numerically due to roundoff. For example, and\n assuming a platform on which a Python float is an IEEE 754 double-\n precision number, in order that ``-1e-100 % 1e100`` have the same\n sign as ``1e100``, the computed result is ``-1e-100 + 1e100``,\n which is numerically exactly equal to ``1e100``. Function\n ``fmod()`` in the ``math`` module returns a result whose sign\n matches the sign of the first argument instead, and so returns\n ``-1e-100`` in this case. Which approach is more appropriate\n depends on the application.\n\n[3] If x is very close to an exact integer multiple of y, it\'s\n possible for ``floor(x/y)`` to be one larger than ``(x-x%y)/y``\n due to rounding. In such cases, Python returns the latter result,\n in order to preserve that ``divmod(x,y)[0] * y + x % y`` be very\n close to ``x``.\n\n[4] While comparisons between unicode strings make sense at the byte\n level, they may be counter-intuitive to users. For example, the\n strings ``u"\\u00C7"`` and ``u"\\u0043\\u0327"`` compare differently,\n even though they both represent the same unicode character (LATIN\n CAPTITAL LETTER C WITH CEDILLA). To compare strings in a human\n recognizable way, compare using ``unicodedata.normalize()``.\n\n[5] The implementation computes this efficiently, without constructing\n lists or sorting.\n\n[6] Earlier versions of Python used lexicographic comparison of the\n sorted (key, value) lists, but this was very expensive for the\n common case of comparing for equality. An even earlier version of\n Python compared dictionaries by identity only, but this caused\n surprises because people expected to be able to test a dictionary\n for emptiness by comparing it to ``{}``.\n\n[7] Due to automatic garbage-collection, free lists, and the dynamic\n nature of descriptors, you may notice seemingly unusual behaviour\n in certain uses of the ``is`` operator, like those involving\n comparisons between instance methods, or constants. Check their\n documentation for more info.\n\n[8] The power operator ``**`` binds less tightly than an arithmetic or\n bitwise unary operator on its right, that is, ``2**-1`` is\n ``0.5``.\n', + 'operator-summary': u'\nSummary\n*******\n\nThe following table summarizes the operator precedences in Python,\nfrom lowest precedence (least binding) to highest precedence (most\nbinding). Operators in the same box have the same precedence. Unless\nthe syntax is explicitly given, operators are binary. Operators in\nthe same box group left to right (except for comparisons, including\ntests, which all have the same precedence and chain from left to right\n--- see section *Comparisons* --- and exponentiation, which groups\nfrom right to left).\n\n+-------------------------------------------------+---------------------------------------+\n| Operator | Description |\n+=================================================+=======================================+\n| ``lambda`` | Lambda expression |\n+-------------------------------------------------+---------------------------------------+\n| ``if`` -- ``else`` | Conditional expression |\n+-------------------------------------------------+---------------------------------------+\n| ``or`` | Boolean OR |\n+-------------------------------------------------+---------------------------------------+\n| ``and`` | Boolean AND |\n+-------------------------------------------------+---------------------------------------+\n| ``not`` *x* | Boolean NOT |\n+-------------------------------------------------+---------------------------------------+\n| ``in``, ``not`` ``in``, ``is``, ``is not``, | Comparisons, including membership |\n| ``<``, ``<=``, ``>``, ``>=``, ``<>``, ``!=``, | tests and identity tests, |\n| ``==`` | |\n+-------------------------------------------------+---------------------------------------+\n| ``|`` | Bitwise OR |\n+-------------------------------------------------+---------------------------------------+\n| ``^`` | Bitwise XOR |\n+-------------------------------------------------+---------------------------------------+\n| ``&`` | Bitwise AND |\n+-------------------------------------------------+---------------------------------------+\n| ``<<``, ``>>`` | Shifts |\n+-------------------------------------------------+---------------------------------------+\n| ``+``, ``-`` | Addition and subtraction |\n+-------------------------------------------------+---------------------------------------+\n| ``*``, ``/``, ``//``, ``%`` | Multiplication, division, remainder |\n+-------------------------------------------------+---------------------------------------+\n| ``+x``, ``-x``, ``~x`` | Positive, negative, bitwise NOT |\n+-------------------------------------------------+---------------------------------------+\n| ``**`` | Exponentiation [8] |\n+-------------------------------------------------+---------------------------------------+\n| ``x[index]``, ``x[index:index]``, | Subscription, slicing, call, |\n| ``x(arguments...)``, ``x.attribute`` | attribute reference |\n+-------------------------------------------------+---------------------------------------+\n| ``(expressions...)``, ``[expressions...]``, | Binding or tuple display, list |\n| ``{key:datum...}``, ```expressions...``` | display, dictionary display, string |\n| | conversion |\n+-------------------------------------------------+---------------------------------------+\n\n-[ Footnotes ]-\n\n[1] In Python 2.3 and later releases, a list comprehension "leaks" the\n control variables of each ``for`` it contains into the containing\n scope. However, this behavior is deprecated, and relying on it\n will not work in Python 3.0\n\n[2] While ``abs(x%y) < abs(y)`` is true mathematically, for floats it\n may not be true numerically due to roundoff. For example, and\n assuming a platform on which a Python float is an IEEE 754 double-\n precision number, in order that ``-1e-100 % 1e100`` have the same\n sign as ``1e100``, the computed result is ``-1e-100 + 1e100``,\n which is numerically exactly equal to ``1e100``. Function\n ``fmod()`` in the ``math`` module returns a result whose sign\n matches the sign of the first argument instead, and so returns\n ``-1e-100`` in this case. Which approach is more appropriate\n depends on the application.\n\n[3] If x is very close to an exact integer multiple of y, it\'s\n possible for ``floor(x/y)`` to be one larger than ``(x-x%y)/y``\n due to rounding. In such cases, Python returns the latter result,\n in order to preserve that ``divmod(x,y)[0] * y + x % y`` be very\n close to ``x``.\n\n[4] While comparisons between unicode strings make sense at the byte\n level, they may be counter-intuitive to users. For example, the\n strings ``u"\\u00C7"`` and ``u"\\u0043\\u0327"`` compare differently,\n even though they both represent the same unicode character (LATIN\n CAPITAL LETTER C WITH CEDILLA). To compare strings in a human\n recognizable way, compare using ``unicodedata.normalize()``.\n\n[5] The implementation computes this efficiently, without constructing\n lists or sorting.\n\n[6] Earlier versions of Python used lexicographic comparison of the\n sorted (key, value) lists, but this was very expensive for the\n common case of comparing for equality. An even earlier version of\n Python compared dictionaries by identity only, but this caused\n surprises because people expected to be able to test a dictionary\n for emptiness by comparing it to ``{}``.\n\n[7] Due to automatic garbage-collection, free lists, and the dynamic\n nature of descriptors, you may notice seemingly unusual behaviour\n in certain uses of the ``is`` operator, like those involving\n comparisons between instance methods, or constants. Check their\n documentation for more info.\n\n[8] The power operator ``**`` binds less tightly than an arithmetic or\n bitwise unary operator on its right, that is, ``2**-1`` is\n ``0.5``.\n', 'pass': u'\nThe ``pass`` statement\n**********************\n\n pass_stmt ::= "pass"\n\n``pass`` is a null operation --- when it is executed, nothing happens.\nIt is useful as a placeholder when a statement is required\nsyntactically, but no code needs to be executed, for example:\n\n def f(arg): pass # a function that does nothing (yet)\n\n class C: pass # a class with no methods (yet)\n', 'power': u'\nThe power operator\n******************\n\nThe power operator binds more tightly than unary operators on its\nleft; it binds less tightly than unary operators on its right. The\nsyntax is:\n\n power ::= primary ["**" u_expr]\n\nThus, in an unparenthesized sequence of power and unary operators, the\noperators are evaluated from right to left (this does not constrain\nthe evaluation order for the operands): ``-1**2`` results in ``-1``.\n\nThe power operator has the same semantics as the built-in ``pow()``\nfunction, when called with two arguments: it yields its left argument\nraised to the power of its right argument. The numeric arguments are\nfirst converted to a common type. The result type is that of the\narguments after coercion.\n\nWith mixed operand types, the coercion rules for binary arithmetic\noperators apply. For int and long int operands, the result has the\nsame type as the operands (after coercion) unless the second argument\nis negative; in that case, all arguments are converted to float and a\nfloat result is delivered. For example, ``10**2`` returns ``100``, but\n``10**-2`` returns ``0.01``. (This last feature was added in Python\n2.2. In Python 2.1 and before, if both arguments were of integer types\nand the second argument was negative, an exception was raised).\n\nRaising ``0.0`` to a negative power results in a\n``ZeroDivisionError``. Raising a negative number to a fractional power\nresults in a ``ValueError``.\n', 'print': u'\nThe ``print`` statement\n***********************\n\n print_stmt ::= "print" ([expression ("," expression)* [","]]\n | ">>" expression [("," expression)+ [","]])\n\n``print`` evaluates each expression in turn and writes the resulting\nobject to standard output (see below). If an object is not a string,\nit is first converted to a string using the rules for string\nconversions. The (resulting or original) string is then written. A\nspace is written before each object is (converted and) written, unless\nthe output system believes it is positioned at the beginning of a\nline. This is the case (1) when no characters have yet been written\nto standard output, (2) when the last character written to standard\noutput is a whitespace character except ``\' \'``, or (3) when the last\nwrite operation on standard output was not a ``print`` statement. (In\nsome cases it may be functional to write an empty string to standard\noutput for this reason.)\n\nNote: Objects which act like file objects but which are not the built-in\n file objects often do not properly emulate this aspect of the file\n object\'s behavior, so it is best not to rely on this.\n\nA ``\'\\n\'`` character is written at the end, unless the ``print``\nstatement ends with a comma. This is the only action if the statement\ncontains just the keyword ``print``.\n\nStandard output is defined as the file object named ``stdout`` in the\nbuilt-in module ``sys``. If no such object exists, or if it does not\nhave a ``write()`` method, a ``RuntimeError`` exception is raised.\n\n``print`` also has an extended form, defined by the second portion of\nthe syntax described above. This form is sometimes referred to as\n"``print`` chevron." In this form, the first expression after the\n``>>`` must evaluate to a "file-like" object, specifically an object\nthat has a ``write()`` method as described above. With this extended\nform, the subsequent expressions are printed to this file object. If\nthe first expression evaluates to ``None``, then ``sys.stdout`` is\nused as the file for output.\n', @@ -62,10 +62,10 @@ 'sequence-types': u"\nEmulating container types\n*************************\n\nThe following methods can be defined to implement container objects.\nContainers usually are sequences (such as lists or tuples) or mappings\n(like dictionaries), but can represent other containers as well. The\nfirst set of methods is used either to emulate a sequence or to\nemulate a mapping; the difference is that for a sequence, the\nallowable keys should be the integers *k* for which ``0 <= k < N``\nwhere *N* is the length of the sequence, or slice objects, which\ndefine a range of items. (For backwards compatibility, the method\n``__getslice__()`` (see below) can also be defined to handle simple,\nbut not extended slices.) It is also recommended that mappings provide\nthe methods ``keys()``, ``values()``, ``items()``, ``has_key()``,\n``get()``, ``clear()``, ``setdefault()``, ``iterkeys()``,\n``itervalues()``, ``iteritems()``, ``pop()``, ``popitem()``,\n``copy()``, and ``update()`` behaving similar to those for Python's\nstandard dictionary objects. The ``UserDict`` module provides a\n``DictMixin`` class to help create those methods from a base set of\n``__getitem__()``, ``__setitem__()``, ``__delitem__()``, and\n``keys()``. Mutable sequences should provide methods ``append()``,\n``count()``, ``index()``, ``extend()``, ``insert()``, ``pop()``,\n``remove()``, ``reverse()`` and ``sort()``, like Python standard list\nobjects. Finally, sequence types should implement addition (meaning\nconcatenation) and multiplication (meaning repetition) by defining the\nmethods ``__add__()``, ``__radd__()``, ``__iadd__()``, ``__mul__()``,\n``__rmul__()`` and ``__imul__()`` described below; they should not\ndefine ``__coerce__()`` or other numerical operators. It is\nrecommended that both mappings and sequences implement the\n``__contains__()`` method to allow efficient use of the ``in``\noperator; for mappings, ``in`` should be equivalent of ``has_key()``;\nfor sequences, it should search through the values. It is further\nrecommended that both mappings and sequences implement the\n``__iter__()`` method to allow efficient iteration through the\ncontainer; for mappings, ``__iter__()`` should be the same as\n``iterkeys()``; for sequences, it should iterate through the values.\n\nobject.__len__(self)\n\n Called to implement the built-in function ``len()``. Should return\n the length of the object, an integer ``>=`` 0. Also, an object\n that doesn't define a ``__nonzero__()`` method and whose\n ``__len__()`` method returns zero is considered to be false in a\n Boolean context.\n\nobject.__getitem__(self, key)\n\n Called to implement evaluation of ``self[key]``. For sequence\n types, the accepted keys should be integers and slice objects.\n Note that the special interpretation of negative indexes (if the\n class wishes to emulate a sequence type) is up to the\n ``__getitem__()`` method. If *key* is of an inappropriate type,\n ``TypeError`` may be raised; if of a value outside the set of\n indexes for the sequence (after any special interpretation of\n negative values), ``IndexError`` should be raised. For mapping\n types, if *key* is missing (not in the container), ``KeyError``\n should be raised.\n\n Note: ``for`` loops expect that an ``IndexError`` will be raised for\n illegal indexes to allow proper detection of the end of the\n sequence.\n\nobject.__setitem__(self, key, value)\n\n Called to implement assignment to ``self[key]``. Same note as for\n ``__getitem__()``. This should only be implemented for mappings if\n the objects support changes to the values for keys, or if new keys\n can be added, or for sequences if elements can be replaced. The\n same exceptions should be raised for improper *key* values as for\n the ``__getitem__()`` method.\n\nobject.__delitem__(self, key)\n\n Called to implement deletion of ``self[key]``. Same note as for\n ``__getitem__()``. This should only be implemented for mappings if\n the objects support removal of keys, or for sequences if elements\n can be removed from the sequence. The same exceptions should be\n raised for improper *key* values as for the ``__getitem__()``\n method.\n\nobject.__iter__(self)\n\n This method is called when an iterator is required for a container.\n This method should return a new iterator object that can iterate\n over all the objects in the container. For mappings, it should\n iterate over the keys of the container, and should also be made\n available as the method ``iterkeys()``.\n\n Iterator objects also need to implement this method; they are\n required to return themselves. For more information on iterator\n objects, see *Iterator Types*.\n\nobject.__reversed__(self)\n\n Called (if present) by the ``reversed()`` built-in to implement\n reverse iteration. It should return a new iterator object that\n iterates over all the objects in the container in reverse order.\n\n If the ``__reversed__()`` method is not provided, the\n ``reversed()`` built-in will fall back to using the sequence\n protocol (``__len__()`` and ``__getitem__()``). Objects that\n support the sequence protocol should only provide\n ``__reversed__()`` if they can provide an implementation that is\n more efficient than the one provided by ``reversed()``.\n\n New in version 2.6.\n\nThe membership test operators (``in`` and ``not in``) are normally\nimplemented as an iteration through a sequence. However, container\nobjects can supply the following special method with a more efficient\nimplementation, which also does not require the object be a sequence.\n\nobject.__contains__(self, item)\n\n Called to implement membership test operators. Should return true\n if *item* is in *self*, false otherwise. For mapping objects, this\n should consider the keys of the mapping rather than the values or\n the key-item pairs.\n\n For objects that don't define ``__contains__()``, the membership\n test first tries iteration via ``__iter__()``, then the old\n sequence iteration protocol via ``__getitem__()``, see *this\n section in the language reference*.\n", 'shifting': u'\nShifting operations\n*******************\n\nThe shifting operations have lower priority than the arithmetic\noperations:\n\n shift_expr ::= a_expr | shift_expr ( "<<" | ">>" ) a_expr\n\nThese operators accept plain or long integers as arguments. The\narguments are converted to a common type. They shift the first\nargument to the left or right by the number of bits given by the\nsecond argument.\n\nA right shift by *n* bits is defined as division by ``pow(2, n)``. A\nleft shift by *n* bits is defined as multiplication with ``pow(2,\nn)``. Negative shift counts raise a ``ValueError`` exception.\n', 'slicings': u'\nSlicings\n********\n\nA slicing selects a range of items in a sequence object (e.g., a\nstring, tuple or list). Slicings may be used as expressions or as\ntargets in assignment or ``del`` statements. The syntax for a\nslicing:\n\n slicing ::= simple_slicing | extended_slicing\n simple_slicing ::= primary "[" short_slice "]"\n extended_slicing ::= primary "[" slice_list "]"\n slice_list ::= slice_item ("," slice_item)* [","]\n slice_item ::= expression | proper_slice | ellipsis\n proper_slice ::= short_slice | long_slice\n short_slice ::= [lower_bound] ":" [upper_bound]\n long_slice ::= short_slice ":" [stride]\n lower_bound ::= expression\n upper_bound ::= expression\n stride ::= expression\n ellipsis ::= "..."\n\nThere is ambiguity in the formal syntax here: anything that looks like\nan expression list also looks like a slice list, so any subscription\ncan be interpreted as a slicing. Rather than further complicating the\nsyntax, this is disambiguated by defining that in this case the\ninterpretation as a subscription takes priority over the\ninterpretation as a slicing (this is the case if the slice list\ncontains no proper slice nor ellipses). Similarly, when the slice\nlist has exactly one short slice and no trailing comma, the\ninterpretation as a simple slicing takes priority over that as an\nextended slicing.\n\nThe semantics for a simple slicing are as follows. The primary must\nevaluate to a sequence object. The lower and upper bound expressions,\nif present, must evaluate to plain integers; defaults are zero and the\n``sys.maxint``, respectively. If either bound is negative, the\nsequence\'s length is added to it. The slicing now selects all items\nwith index *k* such that ``i <= k < j`` where *i* and *j* are the\nspecified lower and upper bounds. This may be an empty sequence. It\nis not an error if *i* or *j* lie outside the range of valid indexes\n(such items don\'t exist so they aren\'t selected).\n\nThe semantics for an extended slicing are as follows. The primary\nmust evaluate to a mapping object, and it is indexed with a key that\nis constructed from the slice list, as follows. If the slice list\ncontains at least one comma, the key is a tuple containing the\nconversion of the slice items; otherwise, the conversion of the lone\nslice item is the key. The conversion of a slice item that is an\nexpression is that expression. The conversion of an ellipsis slice\nitem is the built-in ``Ellipsis`` object. The conversion of a proper\nslice is a slice object (see section *The standard type hierarchy*)\nwhose ``start``, ``stop`` and ``step`` attributes are the values of\nthe expressions given as lower bound, upper bound and stride,\nrespectively, substituting ``None`` for missing expressions.\n', - 'specialattrs': u"\nSpecial Attributes\n******************\n\nThe implementation adds a few special read-only attributes to several\nobject types, where they are relevant. Some of these are not reported\nby the ``dir()`` built-in function.\n\nobject.__dict__\n\n A dictionary or other mapping object used to store an object's\n (writable) attributes.\n\nobject.__methods__\n\n Deprecated since version 2.2: Use the built-in function ``dir()``\n to get a list of an object's attributes. This attribute is no\n longer available.\n\nobject.__members__\n\n Deprecated since version 2.2: Use the built-in function ``dir()``\n to get a list of an object's attributes. This attribute is no\n longer available.\n\ninstance.__class__\n\n The class to which a class instance belongs.\n\nclass.__bases__\n\n The tuple of base classes of a class object. If there are no base\n classes, this will be an empty tuple.\n\nclass.__name__\n\n The name of the class or type.\n\nThe following attributes are only supported by *new-style class*es.\n\nclass.__mro__\n\n This attribute is a tuple of classes that are considered when\n looking for base classes during method resolution.\n\nclass.mro()\n\n This method can be overridden by a metaclass to customize the\n method resolution order for its instances. It is called at class\n instantiation, and its result is stored in ``__mro__``.\n\nclass.__subclasses__()\n\n Each new-style class keeps a list of weak references to its\n immediate subclasses. This method returns a list of all those\n references still alive. Example:\n\n >>> int.__subclasses__()\n []\n\n-[ Footnotes ]-\n\n[1] Additional information on these special methods may be found in\n the Python Reference Manual (*Basic customization*).\n\n[2] As a consequence, the list ``[1, 2]`` is considered equal to\n ``[1.0, 2.0]``, and similarly for tuples.\n\n[3] They must have since the parser can't tell the type of the\n operands.\n\n[4] To format only a tuple you should therefore provide a singleton\n tuple whose only element is the tuple to be formatted.\n\n[5] These numbers are fairly arbitrary. They are intended to avoid\n printing endless strings of meaningless digits without hampering\n correct use and without having to know the exact precision of\n floating point values on a particular machine.\n\n[6] The advantage of leaving the newline on is that returning an empty\n string is then an unambiguous EOF indication. It is also possible\n (in cases where it might matter, for example, if you want to make\n an exact copy of a file while scanning its lines) to tell whether\n the last line of a file ended in a newline or not (yes this\n happens!).\n", - 'specialnames': u'\nSpecial method names\n********************\n\nA class can implement certain operations that are invoked by special\nsyntax (such as arithmetic operations or subscripting and slicing) by\ndefining methods with special names. This is Python\'s approach to\n*operator overloading*, allowing classes to define their own behavior\nwith respect to language operators. For instance, if a class defines\na method named ``__getitem__()``, and ``x`` is an instance of this\nclass, then ``x[i]`` is roughly equivalent to ``x.__getitem__(i)`` for\nold-style classes and ``type(x).__getitem__(x, i)`` for new-style\nclasses. Except where mentioned, attempts to execute an operation\nraise an exception when no appropriate method is defined (typically\n``AttributeError`` or ``TypeError``).\n\nWhen implementing a class that emulates any built-in type, it is\nimportant that the emulation only be implemented to the degree that it\nmakes sense for the object being modelled. For example, some\nsequences may work well with retrieval of individual elements, but\nextracting a slice may not make sense. (One example of this is the\n``NodeList`` interface in the W3C\'s Document Object Model.)\n\n\nBasic customization\n===================\n\nobject.__new__(cls[, ...])\n\n Called to create a new instance of class *cls*. ``__new__()`` is a\n static method (special-cased so you need not declare it as such)\n that takes the class of which an instance was requested as its\n first argument. The remaining arguments are those passed to the\n object constructor expression (the call to the class). The return\n value of ``__new__()`` should be the new object instance (usually\n an instance of *cls*).\n\n Typical implementations create a new instance of the class by\n invoking the superclass\'s ``__new__()`` method using\n ``super(currentclass, cls).__new__(cls[, ...])`` with appropriate\n arguments and then modifying the newly-created instance as\n necessary before returning it.\n\n If ``__new__()`` returns an instance of *cls*, then the new\n instance\'s ``__init__()`` method will be invoked like\n ``__init__(self[, ...])``, where *self* is the new instance and the\n remaining arguments are the same as were passed to ``__new__()``.\n\n If ``__new__()`` does not return an instance of *cls*, then the new\n instance\'s ``__init__()`` method will not be invoked.\n\n ``__new__()`` is intended mainly to allow subclasses of immutable\n types (like int, str, or tuple) to customize instance creation. It\n is also commonly overridden in custom metaclasses in order to\n customize class creation.\n\nobject.__init__(self[, ...])\n\n Called when the instance is created. The arguments are those\n passed to the class constructor expression. If a base class has an\n ``__init__()`` method, the derived class\'s ``__init__()`` method,\n if any, must explicitly call it to ensure proper initialization of\n the base class part of the instance; for example:\n ``BaseClass.__init__(self, [args...])``. As a special constraint\n on constructors, no value may be returned; doing so will cause a\n ``TypeError`` to be raised at runtime.\n\nobject.__del__(self)\n\n Called when the instance is about to be destroyed. This is also\n called a destructor. If a base class has a ``__del__()`` method,\n the derived class\'s ``__del__()`` method, if any, must explicitly\n call it to ensure proper deletion of the base class part of the\n instance. Note that it is possible (though not recommended!) for\n the ``__del__()`` method to postpone destruction of the instance by\n creating a new reference to it. It may then be called at a later\n time when this new reference is deleted. It is not guaranteed that\n ``__del__()`` methods are called for objects that still exist when\n the interpreter exits.\n\n Note: ``del x`` doesn\'t directly call ``x.__del__()`` --- the former\n decrements the reference count for ``x`` by one, and the latter\n is only called when ``x``\'s reference count reaches zero. Some\n common situations that may prevent the reference count of an\n object from going to zero include: circular references between\n objects (e.g., a doubly-linked list or a tree data structure with\n parent and child pointers); a reference to the object on the\n stack frame of a function that caught an exception (the traceback\n stored in ``sys.exc_traceback`` keeps the stack frame alive); or\n a reference to the object on the stack frame that raised an\n unhandled exception in interactive mode (the traceback stored in\n ``sys.last_traceback`` keeps the stack frame alive). The first\n situation can only be remedied by explicitly breaking the cycles;\n the latter two situations can be resolved by storing ``None`` in\n ``sys.exc_traceback`` or ``sys.last_traceback``. Circular\n references which are garbage are detected when the option cycle\n detector is enabled (it\'s on by default), but can only be cleaned\n up if there are no Python-level ``__del__()`` methods involved.\n Refer to the documentation for the ``gc`` module for more\n information about how ``__del__()`` methods are handled by the\n cycle detector, particularly the description of the ``garbage``\n value.\n\n Warning: Due to the precarious circumstances under which ``__del__()``\n methods are invoked, exceptions that occur during their execution\n are ignored, and a warning is printed to ``sys.stderr`` instead.\n Also, when ``__del__()`` is invoked in response to a module being\n deleted (e.g., when execution of the program is done), other\n globals referenced by the ``__del__()`` method may already have\n been deleted or in the process of being torn down (e.g. the\n import machinery shutting down). For this reason, ``__del__()``\n methods should do the absolute minimum needed to maintain\n external invariants. Starting with version 1.5, Python\n guarantees that globals whose name begins with a single\n underscore are deleted from their module before other globals are\n deleted; if no other references to such globals exist, this may\n help in assuring that imported modules are still available at the\n time when the ``__del__()`` method is called.\n\nobject.__repr__(self)\n\n Called by the ``repr()`` built-in function and by string\n conversions (reverse quotes) to compute the "official" string\n representation of an object. If at all possible, this should look\n like a valid Python expression that could be used to recreate an\n object with the same value (given an appropriate environment). If\n this is not possible, a string of the form ``<...some useful\n description...>`` should be returned. The return value must be a\n string object. If a class defines ``__repr__()`` but not\n ``__str__()``, then ``__repr__()`` is also used when an "informal"\n string representation of instances of that class is required.\n\n This is typically used for debugging, so it is important that the\n representation is information-rich and unambiguous.\n\nobject.__str__(self)\n\n Called by the ``str()`` built-in function and by the ``print``\n statement to compute the "informal" string representation of an\n object. This differs from ``__repr__()`` in that it does not have\n to be a valid Python expression: a more convenient or concise\n representation may be used instead. The return value must be a\n string object.\n\nobject.__lt__(self, other)\nobject.__le__(self, other)\nobject.__eq__(self, other)\nobject.__ne__(self, other)\nobject.__gt__(self, other)\nobject.__ge__(self, other)\n\n New in version 2.1.\n\n These are the so-called "rich comparison" methods, and are called\n for comparison operators in preference to ``__cmp__()`` below. The\n correspondence between operator symbols and method names is as\n follows: ``xy`` call ``x.__ne__(y)``, ``x>y`` calls ``x.__gt__(y)``, and\n ``x>=y`` calls ``x.__ge__(y)``.\n\n A rich comparison method may return the singleton\n ``NotImplemented`` if it does not implement the operation for a\n given pair of arguments. By convention, ``False`` and ``True`` are\n returned for a successful comparison. However, these methods can\n return any value, so if the comparison operator is used in a\n Boolean context (e.g., in the condition of an ``if`` statement),\n Python will call ``bool()`` on the value to determine if the result\n is true or false.\n\n There are no implied relationships among the comparison operators.\n The truth of ``x==y`` does not imply that ``x!=y`` is false.\n Accordingly, when defining ``__eq__()``, one should also define\n ``__ne__()`` so that the operators will behave as expected. See\n the paragraph on ``__hash__()`` for some important notes on\n creating *hashable* objects which support custom comparison\n operations and are usable as dictionary keys.\n\n There are no swapped-argument versions of these methods (to be used\n when the left argument does not support the operation but the right\n argument does); rather, ``__lt__()`` and ``__gt__()`` are each\n other\'s reflection, ``__le__()`` and ``__ge__()`` are each other\'s\n reflection, and ``__eq__()`` and ``__ne__()`` are their own\n reflection.\n\n Arguments to rich comparison methods are never coerced.\n\n To automatically generate ordering operations from a single root\n operation, see the Total Ordering recipe in the ASPN cookbook.\n\nobject.__cmp__(self, other)\n\n Called by comparison operations if rich comparison (see above) is\n not defined. Should return a negative integer if ``self < other``,\n zero if ``self == other``, a positive integer if ``self > other``.\n If no ``__cmp__()``, ``__eq__()`` or ``__ne__()`` operation is\n defined, class instances are compared by object identity\n ("address"). See also the description of ``__hash__()`` for some\n important notes on creating *hashable* objects which support custom\n comparison operations and are usable as dictionary keys. (Note: the\n restriction that exceptions are not propagated by ``__cmp__()`` has\n been removed since Python 1.5.)\n\nobject.__rcmp__(self, other)\n\n Changed in version 2.1: No longer supported.\n\nobject.__hash__(self)\n\n Called by built-in function ``hash()`` and for operations on\n members of hashed collections including ``set``, ``frozenset``, and\n ``dict``. ``__hash__()`` should return an integer. The only\n required property is that objects which compare equal have the same\n hash value; it is advised to somehow mix together (e.g. using\n exclusive or) the hash values for the components of the object that\n also play a part in comparison of objects.\n\n If a class does not define a ``__cmp__()`` or ``__eq__()`` method\n it should not define a ``__hash__()`` operation either; if it\n defines ``__cmp__()`` or ``__eq__()`` but not ``__hash__()``, its\n instances will not be usable in hashed collections. If a class\n defines mutable objects and implements a ``__cmp__()`` or\n ``__eq__()`` method, it should not implement ``__hash__()``, since\n hashable collection implementations require that a object\'s hash\n value is immutable (if the object\'s hash value changes, it will be\n in the wrong hash bucket).\n\n User-defined classes have ``__cmp__()`` and ``__hash__()`` methods\n by default; with them, all objects compare unequal (except with\n themselves) and ``x.__hash__()`` returns ``id(x)``.\n\n Classes which inherit a ``__hash__()`` method from a parent class\n but change the meaning of ``__cmp__()`` or ``__eq__()`` such that\n the hash value returned is no longer appropriate (e.g. by switching\n to a value-based concept of equality instead of the default\n identity based equality) can explicitly flag themselves as being\n unhashable by setting ``__hash__ = None`` in the class definition.\n Doing so means that not only will instances of the class raise an\n appropriate ``TypeError`` when a program attempts to retrieve their\n hash value, but they will also be correctly identified as\n unhashable when checking ``isinstance(obj, collections.Hashable)``\n (unlike classes which define their own ``__hash__()`` to explicitly\n raise ``TypeError``).\n\n Changed in version 2.5: ``__hash__()`` may now also return a long\n integer object; the 32-bit integer is then derived from the hash of\n that object.\n\n Changed in version 2.6: ``__hash__`` may now be set to ``None`` to\n explicitly flag instances of a class as unhashable.\n\nobject.__nonzero__(self)\n\n Called to implement truth value testing and the built-in operation\n ``bool()``; should return ``False`` or ``True``, or their integer\n equivalents ``0`` or ``1``. When this method is not defined,\n ``__len__()`` is called, if it is defined, and the object is\n considered true if its result is nonzero. If a class defines\n neither ``__len__()`` nor ``__nonzero__()``, all its instances are\n considered true.\n\nobject.__unicode__(self)\n\n Called to implement ``unicode()`` built-in; should return a Unicode\n object. When this method is not defined, string conversion is\n attempted, and the result of string conversion is converted to\n Unicode using the system default encoding.\n\n\nCustomizing attribute access\n============================\n\nThe following methods can be defined to customize the meaning of\nattribute access (use of, assignment to, or deletion of ``x.name``)\nfor class instances.\n\nobject.__getattr__(self, name)\n\n Called when an attribute lookup has not found the attribute in the\n usual places (i.e. it is not an instance attribute nor is it found\n in the class tree for ``self``). ``name`` is the attribute name.\n This method should return the (computed) attribute value or raise\n an ``AttributeError`` exception.\n\n Note that if the attribute is found through the normal mechanism,\n ``__getattr__()`` is not called. (This is an intentional asymmetry\n between ``__getattr__()`` and ``__setattr__()``.) This is done both\n for efficiency reasons and because otherwise ``__getattr__()``\n would have no way to access other attributes of the instance. Note\n that at least for instance variables, you can fake total control by\n not inserting any values in the instance attribute dictionary (but\n instead inserting them in another object). See the\n ``__getattribute__()`` method below for a way to actually get total\n control in new-style classes.\n\nobject.__setattr__(self, name, value)\n\n Called when an attribute assignment is attempted. This is called\n instead of the normal mechanism (i.e. store the value in the\n instance dictionary). *name* is the attribute name, *value* is the\n value to be assigned to it.\n\n If ``__setattr__()`` wants to assign to an instance attribute, it\n should not simply execute ``self.name = value`` --- this would\n cause a recursive call to itself. Instead, it should insert the\n value in the dictionary of instance attributes, e.g.,\n ``self.__dict__[name] = value``. For new-style classes, rather\n than accessing the instance dictionary, it should call the base\n class method with the same name, for example,\n ``object.__setattr__(self, name, value)``.\n\nobject.__delattr__(self, name)\n\n Like ``__setattr__()`` but for attribute deletion instead of\n assignment. This should only be implemented if ``del obj.name`` is\n meaningful for the object.\n\n\nMore attribute access for new-style classes\n-------------------------------------------\n\nThe following methods only apply to new-style classes.\n\nobject.__getattribute__(self, name)\n\n Called unconditionally to implement attribute accesses for\n instances of the class. If the class also defines\n ``__getattr__()``, the latter will not be called unless\n ``__getattribute__()`` either calls it explicitly or raises an\n ``AttributeError``. This method should return the (computed)\n attribute value or raise an ``AttributeError`` exception. In order\n to avoid infinite recursion in this method, its implementation\n should always call the base class method with the same name to\n access any attributes it needs, for example,\n ``object.__getattribute__(self, name)``.\n\n Note: This method may still be bypassed when looking up special methods\n as the result of implicit invocation via language syntax or\n built-in functions. See *Special method lookup for new-style\n classes*.\n\n\nImplementing Descriptors\n------------------------\n\nThe following methods only apply when an instance of the class\ncontaining the method (a so-called *descriptor* class) appears in the\nclass dictionary of another new-style class, known as the *owner*\nclass. In the examples below, "the attribute" refers to the attribute\nwhose name is the key of the property in the owner class\'\n``__dict__``. Descriptors can only be implemented as new-style\nclasses themselves.\n\nobject.__get__(self, instance, owner)\n\n Called to get the attribute of the owner class (class attribute\n access) or of an instance of that class (instance attribute\n access). *owner* is always the owner class, while *instance* is the\n instance that the attribute was accessed through, or ``None`` when\n the attribute is accessed through the *owner*. This method should\n return the (computed) attribute value or raise an\n ``AttributeError`` exception.\n\nobject.__set__(self, instance, value)\n\n Called to set the attribute on an instance *instance* of the owner\n class to a new value, *value*.\n\nobject.__delete__(self, instance)\n\n Called to delete the attribute on an instance *instance* of the\n owner class.\n\n\nInvoking Descriptors\n--------------------\n\nIn general, a descriptor is an object attribute with "binding\nbehavior", one whose attribute access has been overridden by methods\nin the descriptor protocol: ``__get__()``, ``__set__()``, and\n``__delete__()``. If any of those methods are defined for an object,\nit is said to be a descriptor.\n\nThe default behavior for attribute access is to get, set, or delete\nthe attribute from an object\'s dictionary. For instance, ``a.x`` has a\nlookup chain starting with ``a.__dict__[\'x\']``, then\n``type(a).__dict__[\'x\']``, and continuing through the base classes of\n``type(a)`` excluding metaclasses.\n\nHowever, if the looked-up value is an object defining one of the\ndescriptor methods, then Python may override the default behavior and\ninvoke the descriptor method instead. Where this occurs in the\nprecedence chain depends on which descriptor methods were defined and\nhow they were called. Note that descriptors are only invoked for new\nstyle objects or classes (ones that subclass ``object()`` or\n``type()``).\n\nThe starting point for descriptor invocation is a binding, ``a.x``.\nHow the arguments are assembled depends on ``a``:\n\nDirect Call\n The simplest and least common call is when user code directly\n invokes a descriptor method: ``x.__get__(a)``.\n\nInstance Binding\n If binding to a new-style object instance, ``a.x`` is transformed\n into the call: ``type(a).__dict__[\'x\'].__get__(a, type(a))``.\n\nClass Binding\n If binding to a new-style class, ``A.x`` is transformed into the\n call: ``A.__dict__[\'x\'].__get__(None, A)``.\n\nSuper Binding\n If ``a`` is an instance of ``super``, then the binding ``super(B,\n obj).m()`` searches ``obj.__class__.__mro__`` for the base class\n ``A`` immediately preceding ``B`` and then invokes the descriptor\n with the call: ``A.__dict__[\'m\'].__get__(obj, A)``.\n\nFor instance bindings, the precedence of descriptor invocation depends\non the which descriptor methods are defined. Normally, data\ndescriptors define both ``__get__()`` and ``__set__()``, while non-\ndata descriptors have just the ``__get__()`` method. Data descriptors\nalways override a redefinition in an instance dictionary. In\ncontrast, non-data descriptors can be overridden by instances. [2]\n\nPython methods (including ``staticmethod()`` and ``classmethod()``)\nare implemented as non-data descriptors. Accordingly, instances can\nredefine and override methods. This allows individual instances to\nacquire behaviors that differ from other instances of the same class.\n\nThe ``property()`` function is implemented as a data descriptor.\nAccordingly, instances cannot override the behavior of a property.\n\n\n__slots__\n---------\n\nBy default, instances of both old and new-style classes have a\ndictionary for attribute storage. This wastes space for objects\nhaving very few instance variables. The space consumption can become\nacute when creating large numbers of instances.\n\nThe default can be overridden by defining *__slots__* in a new-style\nclass definition. The *__slots__* declaration takes a sequence of\ninstance variables and reserves just enough space in each instance to\nhold a value for each variable. Space is saved because *__dict__* is\nnot created for each instance.\n\n__slots__\n\n This class variable can be assigned a string, iterable, or sequence\n of strings with variable names used by instances. If defined in a\n new-style class, *__slots__* reserves space for the declared\n variables and prevents the automatic creation of *__dict__* and\n *__weakref__* for each instance.\n\n New in version 2.2.\n\nNotes on using *__slots__*\n\n* When inheriting from a class without *__slots__*, the *__dict__*\n attribute of that class will always be accessible, so a *__slots__*\n definition in the subclass is meaningless.\n\n* Without a *__dict__* variable, instances cannot be assigned new\n variables not listed in the *__slots__* definition. Attempts to\n assign to an unlisted variable name raises ``AttributeError``. If\n dynamic assignment of new variables is desired, then add\n ``\'__dict__\'`` to the sequence of strings in the *__slots__*\n declaration.\n\n Changed in version 2.3: Previously, adding ``\'__dict__\'`` to the\n *__slots__* declaration would not enable the assignment of new\n attributes not specifically listed in the sequence of instance\n variable names.\n\n* Without a *__weakref__* variable for each instance, classes defining\n *__slots__* do not support weak references to its instances. If weak\n reference support is needed, then add ``\'__weakref__\'`` to the\n sequence of strings in the *__slots__* declaration.\n\n Changed in version 2.3: Previously, adding ``\'__weakref__\'`` to the\n *__slots__* declaration would not enable support for weak\n references.\n\n* *__slots__* are implemented at the class level by creating\n descriptors (*Implementing Descriptors*) for each variable name. As\n a result, class attributes cannot be used to set default values for\n instance variables defined by *__slots__*; otherwise, the class\n attribute would overwrite the descriptor assignment.\n\n* The action of a *__slots__* declaration is limited to the class\n where it is defined. As a result, subclasses will have a *__dict__*\n unless they also define *__slots__* (which must only contain names\n of any *additional* slots).\n\n* If a class defines a slot also defined in a base class, the instance\n variable defined by the base class slot is inaccessible (except by\n retrieving its descriptor directly from the base class). This\n renders the meaning of the program undefined. In the future, a\n check may be added to prevent this.\n\n* Nonempty *__slots__* does not work for classes derived from\n "variable-length" built-in types such as ``long``, ``str`` and\n ``tuple``.\n\n* Any non-string iterable may be assigned to *__slots__*. Mappings may\n also be used; however, in the future, special meaning may be\n assigned to the values corresponding to each key.\n\n* *__class__* assignment works only if both classes have the same\n *__slots__*.\n\n Changed in version 2.6: Previously, *__class__* assignment raised an\n error if either new or old class had *__slots__*.\n\n\nCustomizing class creation\n==========================\n\nBy default, new-style classes are constructed using ``type()``. A\nclass definition is read into a separate namespace and the value of\nclass name is bound to the result of ``type(name, bases, dict)``.\n\nWhen the class definition is read, if *__metaclass__* is defined then\nthe callable assigned to it will be called instead of ``type()``. This\nallows classes or functions to be written which monitor or alter the\nclass creation process:\n\n* Modifying the class dictionary prior to the class being created.\n\n* Returning an instance of another class -- essentially performing the\n role of a factory function.\n\nThese steps will have to be performed in the metaclass\'s ``__new__()``\nmethod -- ``type.__new__()`` can then be called from this method to\ncreate a class with different properties. This example adds a new\nelement to the class dictionary before creating the class:\n\n class metacls(type):\n def __new__(mcs, name, bases, dict):\n dict[\'foo\'] = \'metacls was here\'\n return type.__new__(mcs, name, bases, dict)\n\nYou can of course also override other class methods (or add new\nmethods); for example defining a custom ``__call__()`` method in the\nmetaclass allows custom behavior when the class is called, e.g. not\nalways creating a new instance.\n\n__metaclass__\n\n This variable can be any callable accepting arguments for ``name``,\n ``bases``, and ``dict``. Upon class creation, the callable is used\n instead of the built-in ``type()``.\n\n New in version 2.2.\n\nThe appropriate metaclass is determined by the following precedence\nrules:\n\n* If ``dict[\'__metaclass__\']`` exists, it is used.\n\n* Otherwise, if there is at least one base class, its metaclass is\n used (this looks for a *__class__* attribute first and if not found,\n uses its type).\n\n* Otherwise, if a global variable named __metaclass__ exists, it is\n used.\n\n* Otherwise, the old-style, classic metaclass (types.ClassType) is\n used.\n\nThe potential uses for metaclasses are boundless. Some ideas that have\nbeen explored including logging, interface checking, automatic\ndelegation, automatic property creation, proxies, frameworks, and\nautomatic resource locking/synchronization.\n\n\nEmulating callable objects\n==========================\n\nobject.__call__(self[, args...])\n\n Called when the instance is "called" as a function; if this method\n is defined, ``x(arg1, arg2, ...)`` is a shorthand for\n ``x.__call__(arg1, arg2, ...)``.\n\n\nEmulating container types\n=========================\n\nThe following methods can be defined to implement container objects.\nContainers usually are sequences (such as lists or tuples) or mappings\n(like dictionaries), but can represent other containers as well. The\nfirst set of methods is used either to emulate a sequence or to\nemulate a mapping; the difference is that for a sequence, the\nallowable keys should be the integers *k* for which ``0 <= k < N``\nwhere *N* is the length of the sequence, or slice objects, which\ndefine a range of items. (For backwards compatibility, the method\n``__getslice__()`` (see below) can also be defined to handle simple,\nbut not extended slices.) It is also recommended that mappings provide\nthe methods ``keys()``, ``values()``, ``items()``, ``has_key()``,\n``get()``, ``clear()``, ``setdefault()``, ``iterkeys()``,\n``itervalues()``, ``iteritems()``, ``pop()``, ``popitem()``,\n``copy()``, and ``update()`` behaving similar to those for Python\'s\nstandard dictionary objects. The ``UserDict`` module provides a\n``DictMixin`` class to help create those methods from a base set of\n``__getitem__()``, ``__setitem__()``, ``__delitem__()``, and\n``keys()``. Mutable sequences should provide methods ``append()``,\n``count()``, ``index()``, ``extend()``, ``insert()``, ``pop()``,\n``remove()``, ``reverse()`` and ``sort()``, like Python standard list\nobjects. Finally, sequence types should implement addition (meaning\nconcatenation) and multiplication (meaning repetition) by defining the\nmethods ``__add__()``, ``__radd__()``, ``__iadd__()``, ``__mul__()``,\n``__rmul__()`` and ``__imul__()`` described below; they should not\ndefine ``__coerce__()`` or other numerical operators. It is\nrecommended that both mappings and sequences implement the\n``__contains__()`` method to allow efficient use of the ``in``\noperator; for mappings, ``in`` should be equivalent of ``has_key()``;\nfor sequences, it should search through the values. It is further\nrecommended that both mappings and sequences implement the\n``__iter__()`` method to allow efficient iteration through the\ncontainer; for mappings, ``__iter__()`` should be the same as\n``iterkeys()``; for sequences, it should iterate through the values.\n\nobject.__len__(self)\n\n Called to implement the built-in function ``len()``. Should return\n the length of the object, an integer ``>=`` 0. Also, an object\n that doesn\'t define a ``__nonzero__()`` method and whose\n ``__len__()`` method returns zero is considered to be false in a\n Boolean context.\n\nobject.__getitem__(self, key)\n\n Called to implement evaluation of ``self[key]``. For sequence\n types, the accepted keys should be integers and slice objects.\n Note that the special interpretation of negative indexes (if the\n class wishes to emulate a sequence type) is up to the\n ``__getitem__()`` method. If *key* is of an inappropriate type,\n ``TypeError`` may be raised; if of a value outside the set of\n indexes for the sequence (after any special interpretation of\n negative values), ``IndexError`` should be raised. For mapping\n types, if *key* is missing (not in the container), ``KeyError``\n should be raised.\n\n Note: ``for`` loops expect that an ``IndexError`` will be raised for\n illegal indexes to allow proper detection of the end of the\n sequence.\n\nobject.__setitem__(self, key, value)\n\n Called to implement assignment to ``self[key]``. Same note as for\n ``__getitem__()``. This should only be implemented for mappings if\n the objects support changes to the values for keys, or if new keys\n can be added, or for sequences if elements can be replaced. The\n same exceptions should be raised for improper *key* values as for\n the ``__getitem__()`` method.\n\nobject.__delitem__(self, key)\n\n Called to implement deletion of ``self[key]``. Same note as for\n ``__getitem__()``. This should only be implemented for mappings if\n the objects support removal of keys, or for sequences if elements\n can be removed from the sequence. The same exceptions should be\n raised for improper *key* values as for the ``__getitem__()``\n method.\n\nobject.__iter__(self)\n\n This method is called when an iterator is required for a container.\n This method should return a new iterator object that can iterate\n over all the objects in the container. For mappings, it should\n iterate over the keys of the container, and should also be made\n available as the method ``iterkeys()``.\n\n Iterator objects also need to implement this method; they are\n required to return themselves. For more information on iterator\n objects, see *Iterator Types*.\n\nobject.__reversed__(self)\n\n Called (if present) by the ``reversed()`` built-in to implement\n reverse iteration. It should return a new iterator object that\n iterates over all the objects in the container in reverse order.\n\n If the ``__reversed__()`` method is not provided, the\n ``reversed()`` built-in will fall back to using the sequence\n protocol (``__len__()`` and ``__getitem__()``). Objects that\n support the sequence protocol should only provide\n ``__reversed__()`` if they can provide an implementation that is\n more efficient than the one provided by ``reversed()``.\n\n New in version 2.6.\n\nThe membership test operators (``in`` and ``not in``) are normally\nimplemented as an iteration through a sequence. However, container\nobjects can supply the following special method with a more efficient\nimplementation, which also does not require the object be a sequence.\n\nobject.__contains__(self, item)\n\n Called to implement membership test operators. Should return true\n if *item* is in *self*, false otherwise. For mapping objects, this\n should consider the keys of the mapping rather than the values or\n the key-item pairs.\n\n For objects that don\'t define ``__contains__()``, the membership\n test first tries iteration via ``__iter__()``, then the old\n sequence iteration protocol via ``__getitem__()``, see *this\n section in the language reference*.\n\n\nAdditional methods for emulation of sequence types\n==================================================\n\nThe following optional methods can be defined to further emulate\nsequence objects. Immutable sequences methods should at most only\ndefine ``__getslice__()``; mutable sequences might define all three\nmethods.\n\nobject.__getslice__(self, i, j)\n\n Deprecated since version 2.0: Support slice objects as parameters\n to the ``__getitem__()`` method. (However, built-in types in\n CPython currently still implement ``__getslice__()``. Therefore,\n you have to override it in derived classes when implementing\n slicing.)\n\n Called to implement evaluation of ``self[i:j]``. The returned\n object should be of the same type as *self*. Note that missing *i*\n or *j* in the slice expression are replaced by zero or\n ``sys.maxint``, respectively. If negative indexes are used in the\n slice, the length of the sequence is added to that index. If the\n instance does not implement the ``__len__()`` method, an\n ``AttributeError`` is raised. No guarantee is made that indexes\n adjusted this way are not still negative. Indexes which are\n greater than the length of the sequence are not modified. If no\n ``__getslice__()`` is found, a slice object is created instead, and\n passed to ``__getitem__()`` instead.\n\nobject.__setslice__(self, i, j, sequence)\n\n Called to implement assignment to ``self[i:j]``. Same notes for *i*\n and *j* as for ``__getslice__()``.\n\n This method is deprecated. If no ``__setslice__()`` is found, or\n for extended slicing of the form ``self[i:j:k]``, a slice object is\n created, and passed to ``__setitem__()``, instead of\n ``__setslice__()`` being called.\n\nobject.__delslice__(self, i, j)\n\n Called to implement deletion of ``self[i:j]``. Same notes for *i*\n and *j* as for ``__getslice__()``. This method is deprecated. If no\n ``__delslice__()`` is found, or for extended slicing of the form\n ``self[i:j:k]``, a slice object is created, and passed to\n ``__delitem__()``, instead of ``__delslice__()`` being called.\n\nNotice that these methods are only invoked when a single slice with a\nsingle colon is used, and the slice method is available. For slice\noperations involving extended slice notation, or in absence of the\nslice methods, ``__getitem__()``, ``__setitem__()`` or\n``__delitem__()`` is called with a slice object as argument.\n\nThe following example demonstrate how to make your program or module\ncompatible with earlier versions of Python (assuming that methods\n``__getitem__()``, ``__setitem__()`` and ``__delitem__()`` support\nslice objects as arguments):\n\n class MyClass:\n ...\n def __getitem__(self, index):\n ...\n def __setitem__(self, index, value):\n ...\n def __delitem__(self, index):\n ...\n\n if sys.version_info < (2, 0):\n # They won\'t be defined if version is at least 2.0 final\n\n def __getslice__(self, i, j):\n return self[max(0, i):max(0, j):]\n def __setslice__(self, i, j, seq):\n self[max(0, i):max(0, j):] = seq\n def __delslice__(self, i, j):\n del self[max(0, i):max(0, j):]\n ...\n\nNote the calls to ``max()``; these are necessary because of the\nhandling of negative indices before the ``__*slice__()`` methods are\ncalled. When negative indexes are used, the ``__*item__()`` methods\nreceive them as provided, but the ``__*slice__()`` methods get a\n"cooked" form of the index values. For each negative index value, the\nlength of the sequence is added to the index before calling the method\n(which may still result in a negative index); this is the customary\nhandling of negative indexes by the built-in sequence types, and the\n``__*item__()`` methods are expected to do this as well. However,\nsince they should already be doing that, negative indexes cannot be\npassed in; they must be constrained to the bounds of the sequence\nbefore being passed to the ``__*item__()`` methods. Calling ``max(0,\ni)`` conveniently returns the proper value.\n\n\nEmulating numeric types\n=======================\n\nThe following methods can be defined to emulate numeric objects.\nMethods corresponding to operations that are not supported by the\nparticular kind of number implemented (e.g., bitwise operations for\nnon-integral numbers) should be left undefined.\n\nobject.__add__(self, other)\nobject.__sub__(self, other)\nobject.__mul__(self, other)\nobject.__floordiv__(self, other)\nobject.__mod__(self, other)\nobject.__divmod__(self, other)\nobject.__pow__(self, other[, modulo])\nobject.__lshift__(self, other)\nobject.__rshift__(self, other)\nobject.__and__(self, other)\nobject.__xor__(self, other)\nobject.__or__(self, other)\n\n These methods are called to implement the binary arithmetic\n operations (``+``, ``-``, ``*``, ``//``, ``%``, ``divmod()``,\n ``pow()``, ``**``, ``<<``, ``>>``, ``&``, ``^``, ``|``). For\n instance, to evaluate the expression ``x + y``, where *x* is an\n instance of a class that has an ``__add__()`` method,\n ``x.__add__(y)`` is called. The ``__divmod__()`` method should be\n the equivalent to using ``__floordiv__()`` and ``__mod__()``; it\n should not be related to ``__truediv__()`` (described below). Note\n that ``__pow__()`` should be defined to accept an optional third\n argument if the ternary version of the built-in ``pow()`` function\n is to be supported.\n\n If one of those methods does not support the operation with the\n supplied arguments, it should return ``NotImplemented``.\n\nobject.__div__(self, other)\nobject.__truediv__(self, other)\n\n The division operator (``/``) is implemented by these methods. The\n ``__truediv__()`` method is used when ``__future__.division`` is in\n effect, otherwise ``__div__()`` is used. If only one of these two\n methods is defined, the object will not support division in the\n alternate context; ``TypeError`` will be raised instead.\n\nobject.__radd__(self, other)\nobject.__rsub__(self, other)\nobject.__rmul__(self, other)\nobject.__rdiv__(self, other)\nobject.__rtruediv__(self, other)\nobject.__rfloordiv__(self, other)\nobject.__rmod__(self, other)\nobject.__rdivmod__(self, other)\nobject.__rpow__(self, other)\nobject.__rlshift__(self, other)\nobject.__rrshift__(self, other)\nobject.__rand__(self, other)\nobject.__rxor__(self, other)\nobject.__ror__(self, other)\n\n These methods are called to implement the binary arithmetic\n operations (``+``, ``-``, ``*``, ``/``, ``%``, ``divmod()``,\n ``pow()``, ``**``, ``<<``, ``>>``, ``&``, ``^``, ``|``) with\n reflected (swapped) operands. These functions are only called if\n the left operand does not support the corresponding operation and\n the operands are of different types. [3] For instance, to evaluate\n the expression ``x - y``, where *y* is an instance of a class that\n has an ``__rsub__()`` method, ``y.__rsub__(x)`` is called if\n ``x.__sub__(y)`` returns *NotImplemented*.\n\n Note that ternary ``pow()`` will not try calling ``__rpow__()``\n (the coercion rules would become too complicated).\n\n Note: If the right operand\'s type is a subclass of the left operand\'s\n type and that subclass provides the reflected method for the\n operation, this method will be called before the left operand\'s\n non-reflected method. This behavior allows subclasses to\n override their ancestors\' operations.\n\nobject.__iadd__(self, other)\nobject.__isub__(self, other)\nobject.__imul__(self, other)\nobject.__idiv__(self, other)\nobject.__itruediv__(self, other)\nobject.__ifloordiv__(self, other)\nobject.__imod__(self, other)\nobject.__ipow__(self, other[, modulo])\nobject.__ilshift__(self, other)\nobject.__irshift__(self, other)\nobject.__iand__(self, other)\nobject.__ixor__(self, other)\nobject.__ior__(self, other)\n\n These methods are called to implement the augmented arithmetic\n assignments (``+=``, ``-=``, ``*=``, ``/=``, ``//=``, ``%=``,\n ``**=``, ``<<=``, ``>>=``, ``&=``, ``^=``, ``|=``). These methods\n should attempt to do the operation in-place (modifying *self*) and\n return the result (which could be, but does not have to be,\n *self*). If a specific method is not defined, the augmented\n assignment falls back to the normal methods. For instance, to\n execute the statement ``x += y``, where *x* is an instance of a\n class that has an ``__iadd__()`` method, ``x.__iadd__(y)`` is\n called. If *x* is an instance of a class that does not define a\n ``__iadd__()`` method, ``x.__add__(y)`` and ``y.__radd__(x)`` are\n considered, as with the evaluation of ``x + y``.\n\nobject.__neg__(self)\nobject.__pos__(self)\nobject.__abs__(self)\nobject.__invert__(self)\n\n Called to implement the unary arithmetic operations (``-``, ``+``,\n ``abs()`` and ``~``).\n\nobject.__complex__(self)\nobject.__int__(self)\nobject.__long__(self)\nobject.__float__(self)\n\n Called to implement the built-in functions ``complex()``,\n ``int()``, ``long()``, and ``float()``. Should return a value of\n the appropriate type.\n\nobject.__oct__(self)\nobject.__hex__(self)\n\n Called to implement the built-in functions ``oct()`` and ``hex()``.\n Should return a string value.\n\nobject.__index__(self)\n\n Called to implement ``operator.index()``. Also called whenever\n Python needs an integer object (such as in slicing). Must return\n an integer (int or long).\n\n New in version 2.5.\n\nobject.__coerce__(self, other)\n\n Called to implement "mixed-mode" numeric arithmetic. Should either\n return a 2-tuple containing *self* and *other* converted to a\n common numeric type, or ``None`` if conversion is impossible. When\n the common type would be the type of ``other``, it is sufficient to\n return ``None``, since the interpreter will also ask the other\n object to attempt a coercion (but sometimes, if the implementation\n of the other type cannot be changed, it is useful to do the\n conversion to the other type here). A return value of\n ``NotImplemented`` is equivalent to returning ``None``.\n\n\nCoercion rules\n==============\n\nThis section used to document the rules for coercion. As the language\nhas evolved, the coercion rules have become hard to document\nprecisely; documenting what one version of one particular\nimplementation does is undesirable. Instead, here are some informal\nguidelines regarding coercion. In Python 3.0, coercion will not be\nsupported.\n\n* If the left operand of a % operator is a string or Unicode object,\n no coercion takes place and the string formatting operation is\n invoked instead.\n\n* It is no longer recommended to define a coercion operation. Mixed-\n mode operations on types that don\'t define coercion pass the\n original arguments to the operation.\n\n* New-style classes (those derived from ``object``) never invoke the\n ``__coerce__()`` method in response to a binary operator; the only\n time ``__coerce__()`` is invoked is when the built-in function\n ``coerce()`` is called.\n\n* For most intents and purposes, an operator that returns\n ``NotImplemented`` is treated the same as one that is not\n implemented at all.\n\n* Below, ``__op__()`` and ``__rop__()`` are used to signify the\n generic method names corresponding to an operator; ``__iop__()`` is\n used for the corresponding in-place operator. For example, for the\n operator \'``+``\', ``__add__()`` and ``__radd__()`` are used for the\n left and right variant of the binary operator, and ``__iadd__()``\n for the in-place variant.\n\n* For objects *x* and *y*, first ``x.__op__(y)`` is tried. If this is\n not implemented or returns ``NotImplemented``, ``y.__rop__(x)`` is\n tried. If this is also not implemented or returns\n ``NotImplemented``, a ``TypeError`` exception is raised. But see\n the following exception:\n\n* Exception to the previous item: if the left operand is an instance\n of a built-in type or a new-style class, and the right operand is an\n instance of a proper subclass of that type or class and overrides\n the base\'s ``__rop__()`` method, the right operand\'s ``__rop__()``\n method is tried *before* the left operand\'s ``__op__()`` method.\n\n This is done so that a subclass can completely override binary\n operators. Otherwise, the left operand\'s ``__op__()`` method would\n always accept the right operand: when an instance of a given class\n is expected, an instance of a subclass of that class is always\n acceptable.\n\n* When either operand type defines a coercion, this coercion is called\n before that type\'s ``__op__()`` or ``__rop__()`` method is called,\n but no sooner. If the coercion returns an object of a different\n type for the operand whose coercion is invoked, part of the process\n is redone using the new object.\n\n* When an in-place operator (like \'``+=``\') is used, if the left\n operand implements ``__iop__()``, it is invoked without any\n coercion. When the operation falls back to ``__op__()`` and/or\n ``__rop__()``, the normal coercion rules apply.\n\n* In ``x + y``, if *x* is a sequence that implements sequence\n concatenation, sequence concatenation is invoked.\n\n* In ``x * y``, if one operator is a sequence that implements sequence\n repetition, and the other is an integer (``int`` or ``long``),\n sequence repetition is invoked.\n\n* Rich comparisons (implemented by methods ``__eq__()`` and so on)\n never use coercion. Three-way comparison (implemented by\n ``__cmp__()``) does use coercion under the same conditions as other\n binary operations use it.\n\n* In the current implementation, the built-in numeric types ``int``,\n ``long`` and ``float`` do not use coercion; the type ``complex``\n however does use coercion for binary operators and rich comparisons,\n despite the above rules. The difference can become apparent when\n subclassing these types. Over time, the type ``complex`` may be\n fixed to avoid coercion. All these types implement a\n ``__coerce__()`` method, for use by the built-in ``coerce()``\n function.\n\n\nWith Statement Context Managers\n===============================\n\nNew in version 2.5.\n\nA *context manager* is an object that defines the runtime context to\nbe established when executing a ``with`` statement. The context\nmanager handles the entry into, and the exit from, the desired runtime\ncontext for the execution of the block of code. Context managers are\nnormally invoked using the ``with`` statement (described in section\n*The with statement*), but can also be used by directly invoking their\nmethods.\n\nTypical uses of context managers include saving and restoring various\nkinds of global state, locking and unlocking resources, closing opened\nfiles, etc.\n\nFor more information on context managers, see *Context Manager Types*.\n\nobject.__enter__(self)\n\n Enter the runtime context related to this object. The ``with``\n statement will bind this method\'s return value to the target(s)\n specified in the ``as`` clause of the statement, if any.\n\nobject.__exit__(self, exc_type, exc_value, traceback)\n\n Exit the runtime context related to this object. The parameters\n describe the exception that caused the context to be exited. If the\n context was exited without an exception, all three arguments will\n be ``None``.\n\n If an exception is supplied, and the method wishes to suppress the\n exception (i.e., prevent it from being propagated), it should\n return a true value. Otherwise, the exception will be processed\n normally upon exit from this method.\n\n Note that ``__exit__()`` methods should not reraise the passed-in\n exception; this is the caller\'s responsibility.\n\nSee also:\n\n **PEP 0343** - The "with" statement\n The specification, background, and examples for the Python\n ``with`` statement.\n\n\nSpecial method lookup for old-style classes\n===========================================\n\nFor old-style classes, special methods are always looked up in exactly\nthe same way as any other method or attribute. This is the case\nregardless of whether the method is being looked up explicitly as in\n``x.__getitem__(i)`` or implicitly as in ``x[i]``.\n\nThis behaviour means that special methods may exhibit different\nbehaviour for different instances of a single old-style class if the\nappropriate special attributes are set differently:\n\n >>> class C:\n ... pass\n ...\n >>> c1 = C()\n >>> c2 = C()\n >>> c1.__len__ = lambda: 5\n >>> c2.__len__ = lambda: 9\n >>> len(c1)\n 5\n >>> len(c2)\n 9\n\n\nSpecial method lookup for new-style classes\n===========================================\n\nFor new-style classes, implicit invocations of special methods are\nonly guaranteed to work correctly if defined on an object\'s type, not\nin the object\'s instance dictionary. That behaviour is the reason why\nthe following code raises an exception (unlike the equivalent example\nwith old-style classes):\n\n >>> class C(object):\n ... pass\n ...\n >>> c = C()\n >>> c.__len__ = lambda: 5\n >>> len(c)\n Traceback (most recent call last):\n File "", line 1, in \n TypeError: object of type \'C\' has no len()\n\nThe rationale behind this behaviour lies with a number of special\nmethods such as ``__hash__()`` and ``__repr__()`` that are implemented\nby all objects, including type objects. If the implicit lookup of\nthese methods used the conventional lookup process, they would fail\nwhen invoked on the type object itself:\n\n >>> 1 .__hash__() == hash(1)\n True\n >>> int.__hash__() == hash(int)\n Traceback (most recent call last):\n File "", line 1, in \n TypeError: descriptor \'__hash__\' of \'int\' object needs an argument\n\nIncorrectly attempting to invoke an unbound method of a class in this\nway is sometimes referred to as \'metaclass confusion\', and is avoided\nby bypassing the instance when looking up special methods:\n\n >>> type(1).__hash__(1) == hash(1)\n True\n >>> type(int).__hash__(int) == hash(int)\n True\n\nIn addition to bypassing any instance attributes in the interest of\ncorrectness, implicit special method lookup generally also bypasses\nthe ``__getattribute__()`` method even of the object\'s metaclass:\n\n >>> class Meta(type):\n ... def __getattribute__(*args):\n ... print "Metaclass getattribute invoked"\n ... return type.__getattribute__(*args)\n ...\n >>> class C(object):\n ... __metaclass__ = Meta\n ... def __len__(self):\n ... return 10\n ... def __getattribute__(*args):\n ... print "Class getattribute invoked"\n ... return object.__getattribute__(*args)\n ...\n >>> c = C()\n >>> c.__len__() # Explicit lookup via instance\n Class getattribute invoked\n 10\n >>> type(c).__len__(c) # Explicit lookup via type\n Metaclass getattribute invoked\n 10\n >>> len(c) # Implicit lookup\n 10\n\nBypassing the ``__getattribute__()`` machinery in this fashion\nprovides significant scope for speed optimisations within the\ninterpreter, at the cost of some flexibility in the handling of\nspecial methods (the special method *must* be set on the class object\nitself in order to be consistently invoked by the interpreter).\n\n-[ Footnotes ]-\n\n[1] It *is* possible in some cases to change an object\'s type, under\n certain controlled conditions. It generally isn\'t a good idea\n though, since it can lead to some very strange behaviour if it is\n handled incorrectly.\n\n[2] A descriptor can define any combination of ``__get__()``,\n ``__set__()`` and ``__delete__()``. If it does not define\n ``__get__()``, then accessing the attribute even on an instance\n will return the descriptor object itself. If the descriptor\n defines ``__set__()`` and/or ``__delete__()``, it is a data\n descriptor; if it defines neither, it is a non-data descriptor.\n\n[3] For operands of the same type, it is assumed that if the non-\n reflected method (such as ``__add__()``) fails the operation is\n not supported, which is why the reflected method is not called.\n', + 'specialattrs': u"\nSpecial Attributes\n******************\n\nThe implementation adds a few special read-only attributes to several\nobject types, where they are relevant. Some of these are not reported\nby the ``dir()`` built-in function.\n\nobject.__dict__\n\n A dictionary or other mapping object used to store an object's\n (writable) attributes.\n\nobject.__methods__\n\n Deprecated since version 2.2: Use the built-in function ``dir()``\n to get a list of an object's attributes. This attribute is no\n longer available.\n\nobject.__members__\n\n Deprecated since version 2.2: Use the built-in function ``dir()``\n to get a list of an object's attributes. This attribute is no\n longer available.\n\ninstance.__class__\n\n The class to which a class instance belongs.\n\nclass.__bases__\n\n The tuple of base classes of a class object.\n\nclass.__name__\n\n The name of the class or type.\n\nThe following attributes are only supported by *new-style class*es.\n\nclass.__mro__\n\n This attribute is a tuple of classes that are considered when\n looking for base classes during method resolution.\n\nclass.mro()\n\n This method can be overridden by a metaclass to customize the\n method resolution order for its instances. It is called at class\n instantiation, and its result is stored in ``__mro__``.\n\nclass.__subclasses__()\n\n Each new-style class keeps a list of weak references to its\n immediate subclasses. This method returns a list of all those\n references still alive. Example:\n\n >>> int.__subclasses__()\n []\n\n-[ Footnotes ]-\n\n[1] Additional information on these special methods may be found in\n the Python Reference Manual (*Basic customization*).\n\n[2] As a consequence, the list ``[1, 2]`` is considered equal to\n ``[1.0, 2.0]``, and similarly for tuples.\n\n[3] They must have since the parser can't tell the type of the\n operands.\n\n[4] To format only a tuple you should therefore provide a singleton\n tuple whose only element is the tuple to be formatted.\n\n[5] These numbers are fairly arbitrary. They are intended to avoid\n printing endless strings of meaningless digits without hampering\n correct use and without having to know the exact precision of\n floating point values on a particular machine.\n\n[6] The advantage of leaving the newline on is that returning an empty\n string is then an unambiguous EOF indication. It is also possible\n (in cases where it might matter, for example, if you want to make\n an exact copy of a file while scanning its lines) to tell whether\n the last line of a file ended in a newline or not (yes this\n happens!).\n", + 'specialnames': u'\nSpecial method names\n********************\n\nA class can implement certain operations that are invoked by special\nsyntax (such as arithmetic operations or subscripting and slicing) by\ndefining methods with special names. This is Python\'s approach to\n*operator overloading*, allowing classes to define their own behavior\nwith respect to language operators. For instance, if a class defines\na method named ``__getitem__()``, and ``x`` is an instance of this\nclass, then ``x[i]`` is roughly equivalent to ``x.__getitem__(i)`` for\nold-style classes and ``type(x).__getitem__(x, i)`` for new-style\nclasses. Except where mentioned, attempts to execute an operation\nraise an exception when no appropriate method is defined (typically\n``AttributeError`` or ``TypeError``).\n\nWhen implementing a class that emulates any built-in type, it is\nimportant that the emulation only be implemented to the degree that it\nmakes sense for the object being modelled. For example, some\nsequences may work well with retrieval of individual elements, but\nextracting a slice may not make sense. (One example of this is the\n``NodeList`` interface in the W3C\'s Document Object Model.)\n\n\nBasic customization\n===================\n\nobject.__new__(cls[, ...])\n\n Called to create a new instance of class *cls*. ``__new__()`` is a\n static method (special-cased so you need not declare it as such)\n that takes the class of which an instance was requested as its\n first argument. The remaining arguments are those passed to the\n object constructor expression (the call to the class). The return\n value of ``__new__()`` should be the new object instance (usually\n an instance of *cls*).\n\n Typical implementations create a new instance of the class by\n invoking the superclass\'s ``__new__()`` method using\n ``super(currentclass, cls).__new__(cls[, ...])`` with appropriate\n arguments and then modifying the newly-created instance as\n necessary before returning it.\n\n If ``__new__()`` returns an instance of *cls*, then the new\n instance\'s ``__init__()`` method will be invoked like\n ``__init__(self[, ...])``, where *self* is the new instance and the\n remaining arguments are the same as were passed to ``__new__()``.\n\n If ``__new__()`` does not return an instance of *cls*, then the new\n instance\'s ``__init__()`` method will not be invoked.\n\n ``__new__()`` is intended mainly to allow subclasses of immutable\n types (like int, str, or tuple) to customize instance creation. It\n is also commonly overridden in custom metaclasses in order to\n customize class creation.\n\nobject.__init__(self[, ...])\n\n Called when the instance is created. The arguments are those\n passed to the class constructor expression. If a base class has an\n ``__init__()`` method, the derived class\'s ``__init__()`` method,\n if any, must explicitly call it to ensure proper initialization of\n the base class part of the instance; for example:\n ``BaseClass.__init__(self, [args...])``. As a special constraint\n on constructors, no value may be returned; doing so will cause a\n ``TypeError`` to be raised at runtime.\n\nobject.__del__(self)\n\n Called when the instance is about to be destroyed. This is also\n called a destructor. If a base class has a ``__del__()`` method,\n the derived class\'s ``__del__()`` method, if any, must explicitly\n call it to ensure proper deletion of the base class part of the\n instance. Note that it is possible (though not recommended!) for\n the ``__del__()`` method to postpone destruction of the instance by\n creating a new reference to it. It may then be called at a later\n time when this new reference is deleted. It is not guaranteed that\n ``__del__()`` methods are called for objects that still exist when\n the interpreter exits.\n\n Note: ``del x`` doesn\'t directly call ``x.__del__()`` --- the former\n decrements the reference count for ``x`` by one, and the latter\n is only called when ``x``\'s reference count reaches zero. Some\n common situations that may prevent the reference count of an\n object from going to zero include: circular references between\n objects (e.g., a doubly-linked list or a tree data structure with\n parent and child pointers); a reference to the object on the\n stack frame of a function that caught an exception (the traceback\n stored in ``sys.exc_traceback`` keeps the stack frame alive); or\n a reference to the object on the stack frame that raised an\n unhandled exception in interactive mode (the traceback stored in\n ``sys.last_traceback`` keeps the stack frame alive). The first\n situation can only be remedied by explicitly breaking the cycles;\n the latter two situations can be resolved by storing ``None`` in\n ``sys.exc_traceback`` or ``sys.last_traceback``. Circular\n references which are garbage are detected when the option cycle\n detector is enabled (it\'s on by default), but can only be cleaned\n up if there are no Python-level ``__del__()`` methods involved.\n Refer to the documentation for the ``gc`` module for more\n information about how ``__del__()`` methods are handled by the\n cycle detector, particularly the description of the ``garbage``\n value.\n\n Warning: Due to the precarious circumstances under which ``__del__()``\n methods are invoked, exceptions that occur during their execution\n are ignored, and a warning is printed to ``sys.stderr`` instead.\n Also, when ``__del__()`` is invoked in response to a module being\n deleted (e.g., when execution of the program is done), other\n globals referenced by the ``__del__()`` method may already have\n been deleted or in the process of being torn down (e.g. the\n import machinery shutting down). For this reason, ``__del__()``\n methods should do the absolute minimum needed to maintain\n external invariants. Starting with version 1.5, Python\n guarantees that globals whose name begins with a single\n underscore are deleted from their module before other globals are\n deleted; if no other references to such globals exist, this may\n help in assuring that imported modules are still available at the\n time when the ``__del__()`` method is called.\n\nobject.__repr__(self)\n\n Called by the ``repr()`` built-in function and by string\n conversions (reverse quotes) to compute the "official" string\n representation of an object. If at all possible, this should look\n like a valid Python expression that could be used to recreate an\n object with the same value (given an appropriate environment). If\n this is not possible, a string of the form ``<...some useful\n description...>`` should be returned. The return value must be a\n string object. If a class defines ``__repr__()`` but not\n ``__str__()``, then ``__repr__()`` is also used when an "informal"\n string representation of instances of that class is required.\n\n This is typically used for debugging, so it is important that the\n representation is information-rich and unambiguous.\n\nobject.__str__(self)\n\n Called by the ``str()`` built-in function and by the ``print``\n statement to compute the "informal" string representation of an\n object. This differs from ``__repr__()`` in that it does not have\n to be a valid Python expression: a more convenient or concise\n representation may be used instead. The return value must be a\n string object.\n\nobject.__lt__(self, other)\nobject.__le__(self, other)\nobject.__eq__(self, other)\nobject.__ne__(self, other)\nobject.__gt__(self, other)\nobject.__ge__(self, other)\n\n New in version 2.1.\n\n These are the so-called "rich comparison" methods, and are called\n for comparison operators in preference to ``__cmp__()`` below. The\n correspondence between operator symbols and method names is as\n follows: ``xy`` call ``x.__ne__(y)``, ``x>y`` calls ``x.__gt__(y)``, and\n ``x>=y`` calls ``x.__ge__(y)``.\n\n A rich comparison method may return the singleton\n ``NotImplemented`` if it does not implement the operation for a\n given pair of arguments. By convention, ``False`` and ``True`` are\n returned for a successful comparison. However, these methods can\n return any value, so if the comparison operator is used in a\n Boolean context (e.g., in the condition of an ``if`` statement),\n Python will call ``bool()`` on the value to determine if the result\n is true or false.\n\n There are no implied relationships among the comparison operators.\n The truth of ``x==y`` does not imply that ``x!=y`` is false.\n Accordingly, when defining ``__eq__()``, one should also define\n ``__ne__()`` so that the operators will behave as expected. See\n the paragraph on ``__hash__()`` for some important notes on\n creating *hashable* objects which support custom comparison\n operations and are usable as dictionary keys.\n\n There are no swapped-argument versions of these methods (to be used\n when the left argument does not support the operation but the right\n argument does); rather, ``__lt__()`` and ``__gt__()`` are each\n other\'s reflection, ``__le__()`` and ``__ge__()`` are each other\'s\n reflection, and ``__eq__()`` and ``__ne__()`` are their own\n reflection.\n\n Arguments to rich comparison methods are never coerced.\n\n To automatically generate ordering operations from a single root\n operation, see the Total Ordering recipe in the ASPN cookbook.\n\nobject.__cmp__(self, other)\n\n Called by comparison operations if rich comparison (see above) is\n not defined. Should return a negative integer if ``self < other``,\n zero if ``self == other``, a positive integer if ``self > other``.\n If no ``__cmp__()``, ``__eq__()`` or ``__ne__()`` operation is\n defined, class instances are compared by object identity\n ("address"). See also the description of ``__hash__()`` for some\n important notes on creating *hashable* objects which support custom\n comparison operations and are usable as dictionary keys. (Note: the\n restriction that exceptions are not propagated by ``__cmp__()`` has\n been removed since Python 1.5.)\n\nobject.__rcmp__(self, other)\n\n Changed in version 2.1: No longer supported.\n\nobject.__hash__(self)\n\n Called by built-in function ``hash()`` and for operations on\n members of hashed collections including ``set``, ``frozenset``, and\n ``dict``. ``__hash__()`` should return an integer. The only\n required property is that objects which compare equal have the same\n hash value; it is advised to somehow mix together (e.g. using\n exclusive or) the hash values for the components of the object that\n also play a part in comparison of objects.\n\n If a class does not define a ``__cmp__()`` or ``__eq__()`` method\n it should not define a ``__hash__()`` operation either; if it\n defines ``__cmp__()`` or ``__eq__()`` but not ``__hash__()``, its\n instances will not be usable in hashed collections. If a class\n defines mutable objects and implements a ``__cmp__()`` or\n ``__eq__()`` method, it should not implement ``__hash__()``, since\n hashable collection implementations require that a object\'s hash\n value is immutable (if the object\'s hash value changes, it will be\n in the wrong hash bucket).\n\n User-defined classes have ``__cmp__()`` and ``__hash__()`` methods\n by default; with them, all objects compare unequal (except with\n themselves) and ``x.__hash__()`` returns ``id(x)``.\n\n Classes which inherit a ``__hash__()`` method from a parent class\n but change the meaning of ``__cmp__()`` or ``__eq__()`` such that\n the hash value returned is no longer appropriate (e.g. by switching\n to a value-based concept of equality instead of the default\n identity based equality) can explicitly flag themselves as being\n unhashable by setting ``__hash__ = None`` in the class definition.\n Doing so means that not only will instances of the class raise an\n appropriate ``TypeError`` when a program attempts to retrieve their\n hash value, but they will also be correctly identified as\n unhashable when checking ``isinstance(obj, collections.Hashable)``\n (unlike classes which define their own ``__hash__()`` to explicitly\n raise ``TypeError``).\n\n Changed in version 2.5: ``__hash__()`` may now also return a long\n integer object; the 32-bit integer is then derived from the hash of\n that object.\n\n Changed in version 2.6: ``__hash__`` may now be set to ``None`` to\n explicitly flag instances of a class as unhashable.\n\nobject.__nonzero__(self)\n\n Called to implement truth value testing and the built-in operation\n ``bool()``; should return ``False`` or ``True``, or their integer\n equivalents ``0`` or ``1``. When this method is not defined,\n ``__len__()`` is called, if it is defined, and the object is\n considered true if its result is nonzero. If a class defines\n neither ``__len__()`` nor ``__nonzero__()``, all its instances are\n considered true.\n\nobject.__unicode__(self)\n\n Called to implement ``unicode()`` built-in; should return a Unicode\n object. When this method is not defined, string conversion is\n attempted, and the result of string conversion is converted to\n Unicode using the system default encoding.\n\n\nCustomizing attribute access\n============================\n\nThe following methods can be defined to customize the meaning of\nattribute access (use of, assignment to, or deletion of ``x.name``)\nfor class instances.\n\nobject.__getattr__(self, name)\n\n Called when an attribute lookup has not found the attribute in the\n usual places (i.e. it is not an instance attribute nor is it found\n in the class tree for ``self``). ``name`` is the attribute name.\n This method should return the (computed) attribute value or raise\n an ``AttributeError`` exception.\n\n Note that if the attribute is found through the normal mechanism,\n ``__getattr__()`` is not called. (This is an intentional asymmetry\n between ``__getattr__()`` and ``__setattr__()``.) This is done both\n for efficiency reasons and because otherwise ``__getattr__()``\n would have no way to access other attributes of the instance. Note\n that at least for instance variables, you can fake total control by\n not inserting any values in the instance attribute dictionary (but\n instead inserting them in another object). See the\n ``__getattribute__()`` method below for a way to actually get total\n control in new-style classes.\n\nobject.__setattr__(self, name, value)\n\n Called when an attribute assignment is attempted. This is called\n instead of the normal mechanism (i.e. store the value in the\n instance dictionary). *name* is the attribute name, *value* is the\n value to be assigned to it.\n\n If ``__setattr__()`` wants to assign to an instance attribute, it\n should not simply execute ``self.name = value`` --- this would\n cause a recursive call to itself. Instead, it should insert the\n value in the dictionary of instance attributes, e.g.,\n ``self.__dict__[name] = value``. For new-style classes, rather\n than accessing the instance dictionary, it should call the base\n class method with the same name, for example,\n ``object.__setattr__(self, name, value)``.\n\nobject.__delattr__(self, name)\n\n Like ``__setattr__()`` but for attribute deletion instead of\n assignment. This should only be implemented if ``del obj.name`` is\n meaningful for the object.\n\n\nMore attribute access for new-style classes\n-------------------------------------------\n\nThe following methods only apply to new-style classes.\n\nobject.__getattribute__(self, name)\n\n Called unconditionally to implement attribute accesses for\n instances of the class. If the class also defines\n ``__getattr__()``, the latter will not be called unless\n ``__getattribute__()`` either calls it explicitly or raises an\n ``AttributeError``. This method should return the (computed)\n attribute value or raise an ``AttributeError`` exception. In order\n to avoid infinite recursion in this method, its implementation\n should always call the base class method with the same name to\n access any attributes it needs, for example,\n ``object.__getattribute__(self, name)``.\n\n Note: This method may still be bypassed when looking up special methods\n as the result of implicit invocation via language syntax or\n built-in functions. See *Special method lookup for new-style\n classes*.\n\n\nImplementing Descriptors\n------------------------\n\nThe following methods only apply when an instance of the class\ncontaining the method (a so-called *descriptor* class) appears in the\nclass dictionary of another new-style class, known as the *owner*\nclass. In the examples below, "the attribute" refers to the attribute\nwhose name is the key of the property in the owner class\'\n``__dict__``. Descriptors can only be implemented as new-style\nclasses themselves.\n\nobject.__get__(self, instance, owner)\n\n Called to get the attribute of the owner class (class attribute\n access) or of an instance of that class (instance attribute\n access). *owner* is always the owner class, while *instance* is the\n instance that the attribute was accessed through, or ``None`` when\n the attribute is accessed through the *owner*. This method should\n return the (computed) attribute value or raise an\n ``AttributeError`` exception.\n\nobject.__set__(self, instance, value)\n\n Called to set the attribute on an instance *instance* of the owner\n class to a new value, *value*.\n\nobject.__delete__(self, instance)\n\n Called to delete the attribute on an instance *instance* of the\n owner class.\n\n\nInvoking Descriptors\n--------------------\n\nIn general, a descriptor is an object attribute with "binding\nbehavior", one whose attribute access has been overridden by methods\nin the descriptor protocol: ``__get__()``, ``__set__()``, and\n``__delete__()``. If any of those methods are defined for an object,\nit is said to be a descriptor.\n\nThe default behavior for attribute access is to get, set, or delete\nthe attribute from an object\'s dictionary. For instance, ``a.x`` has a\nlookup chain starting with ``a.__dict__[\'x\']``, then\n``type(a).__dict__[\'x\']``, and continuing through the base classes of\n``type(a)`` excluding metaclasses.\n\nHowever, if the looked-up value is an object defining one of the\ndescriptor methods, then Python may override the default behavior and\ninvoke the descriptor method instead. Where this occurs in the\nprecedence chain depends on which descriptor methods were defined and\nhow they were called. Note that descriptors are only invoked for new\nstyle objects or classes (ones that subclass ``object()`` or\n``type()``).\n\nThe starting point for descriptor invocation is a binding, ``a.x``.\nHow the arguments are assembled depends on ``a``:\n\nDirect Call\n The simplest and least common call is when user code directly\n invokes a descriptor method: ``x.__get__(a)``.\n\nInstance Binding\n If binding to a new-style object instance, ``a.x`` is transformed\n into the call: ``type(a).__dict__[\'x\'].__get__(a, type(a))``.\n\nClass Binding\n If binding to a new-style class, ``A.x`` is transformed into the\n call: ``A.__dict__[\'x\'].__get__(None, A)``.\n\nSuper Binding\n If ``a`` is an instance of ``super``, then the binding ``super(B,\n obj).m()`` searches ``obj.__class__.__mro__`` for the base class\n ``A`` immediately preceding ``B`` and then invokes the descriptor\n with the call: ``A.__dict__[\'m\'].__get__(obj, A)``.\n\nFor instance bindings, the precedence of descriptor invocation depends\non the which descriptor methods are defined. A descriptor can define\nany combination of ``__get__()``, ``__set__()`` and ``__delete__()``.\nIf it does not define ``__get__()``, then accessing the attribute will\nreturn the descriptor object itself unless there is a value in the\nobject\'s instance dictionary. If the descriptor defines ``__set__()``\nand/or ``__delete__()``, it is a data descriptor; if it defines\nneither, it is a non-data descriptor. Normally, data descriptors\ndefine both ``__get__()`` and ``__set__()``, while non-data\ndescriptors have just the ``__get__()`` method. Data descriptors with\n``__set__()`` and ``__get__()`` defined always override a redefinition\nin an instance dictionary. In contrast, non-data descriptors can be\noverridden by instances.\n\nPython methods (including ``staticmethod()`` and ``classmethod()``)\nare implemented as non-data descriptors. Accordingly, instances can\nredefine and override methods. This allows individual instances to\nacquire behaviors that differ from other instances of the same class.\n\nThe ``property()`` function is implemented as a data descriptor.\nAccordingly, instances cannot override the behavior of a property.\n\n\n__slots__\n---------\n\nBy default, instances of both old and new-style classes have a\ndictionary for attribute storage. This wastes space for objects\nhaving very few instance variables. The space consumption can become\nacute when creating large numbers of instances.\n\nThe default can be overridden by defining *__slots__* in a new-style\nclass definition. The *__slots__* declaration takes a sequence of\ninstance variables and reserves just enough space in each instance to\nhold a value for each variable. Space is saved because *__dict__* is\nnot created for each instance.\n\n__slots__\n\n This class variable can be assigned a string, iterable, or sequence\n of strings with variable names used by instances. If defined in a\n new-style class, *__slots__* reserves space for the declared\n variables and prevents the automatic creation of *__dict__* and\n *__weakref__* for each instance.\n\n New in version 2.2.\n\nNotes on using *__slots__*\n\n* When inheriting from a class without *__slots__*, the *__dict__*\n attribute of that class will always be accessible, so a *__slots__*\n definition in the subclass is meaningless.\n\n* Without a *__dict__* variable, instances cannot be assigned new\n variables not listed in the *__slots__* definition. Attempts to\n assign to an unlisted variable name raises ``AttributeError``. If\n dynamic assignment of new variables is desired, then add\n ``\'__dict__\'`` to the sequence of strings in the *__slots__*\n declaration.\n\n Changed in version 2.3: Previously, adding ``\'__dict__\'`` to the\n *__slots__* declaration would not enable the assignment of new\n attributes not specifically listed in the sequence of instance\n variable names.\n\n* Without a *__weakref__* variable for each instance, classes defining\n *__slots__* do not support weak references to its instances. If weak\n reference support is needed, then add ``\'__weakref__\'`` to the\n sequence of strings in the *__slots__* declaration.\n\n Changed in version 2.3: Previously, adding ``\'__weakref__\'`` to the\n *__slots__* declaration would not enable support for weak\n references.\n\n* *__slots__* are implemented at the class level by creating\n descriptors (*Implementing Descriptors*) for each variable name. As\n a result, class attributes cannot be used to set default values for\n instance variables defined by *__slots__*; otherwise, the class\n attribute would overwrite the descriptor assignment.\n\n* The action of a *__slots__* declaration is limited to the class\n where it is defined. As a result, subclasses will have a *__dict__*\n unless they also define *__slots__* (which must only contain names\n of any *additional* slots).\n\n* If a class defines a slot also defined in a base class, the instance\n variable defined by the base class slot is inaccessible (except by\n retrieving its descriptor directly from the base class). This\n renders the meaning of the program undefined. In the future, a\n check may be added to prevent this.\n\n* Nonempty *__slots__* does not work for classes derived from\n "variable-length" built-in types such as ``long``, ``str`` and\n ``tuple``.\n\n* Any non-string iterable may be assigned to *__slots__*. Mappings may\n also be used; however, in the future, special meaning may be\n assigned to the values corresponding to each key.\n\n* *__class__* assignment works only if both classes have the same\n *__slots__*.\n\n Changed in version 2.6: Previously, *__class__* assignment raised an\n error if either new or old class had *__slots__*.\n\n\nCustomizing class creation\n==========================\n\nBy default, new-style classes are constructed using ``type()``. A\nclass definition is read into a separate namespace and the value of\nclass name is bound to the result of ``type(name, bases, dict)``.\n\nWhen the class definition is read, if *__metaclass__* is defined then\nthe callable assigned to it will be called instead of ``type()``. This\nallows classes or functions to be written which monitor or alter the\nclass creation process:\n\n* Modifying the class dictionary prior to the class being created.\n\n* Returning an instance of another class -- essentially performing the\n role of a factory function.\n\nThese steps will have to be performed in the metaclass\'s ``__new__()``\nmethod -- ``type.__new__()`` can then be called from this method to\ncreate a class with different properties. This example adds a new\nelement to the class dictionary before creating the class:\n\n class metacls(type):\n def __new__(mcs, name, bases, dict):\n dict[\'foo\'] = \'metacls was here\'\n return type.__new__(mcs, name, bases, dict)\n\nYou can of course also override other class methods (or add new\nmethods); for example defining a custom ``__call__()`` method in the\nmetaclass allows custom behavior when the class is called, e.g. not\nalways creating a new instance.\n\n__metaclass__\n\n This variable can be any callable accepting arguments for ``name``,\n ``bases``, and ``dict``. Upon class creation, the callable is used\n instead of the built-in ``type()``.\n\n New in version 2.2.\n\nThe appropriate metaclass is determined by the following precedence\nrules:\n\n* If ``dict[\'__metaclass__\']`` exists, it is used.\n\n* Otherwise, if there is at least one base class, its metaclass is\n used (this looks for a *__class__* attribute first and if not found,\n uses its type).\n\n* Otherwise, if a global variable named __metaclass__ exists, it is\n used.\n\n* Otherwise, the old-style, classic metaclass (types.ClassType) is\n used.\n\nThe potential uses for metaclasses are boundless. Some ideas that have\nbeen explored including logging, interface checking, automatic\ndelegation, automatic property creation, proxies, frameworks, and\nautomatic resource locking/synchronization.\n\n\nCustomizing instance and subclass checks\n========================================\n\nNew in version 2.6.\n\nThe following methods are used to override the default behavior of the\n``isinstance()`` and ``issubclass()`` built-in functions.\n\nIn particular, the metaclass ``abc.ABCMeta`` implements these methods\nin order to allow the addition of Abstract Base Classes (ABCs) as\n"virtual base classes" to any class or type (including built-in\ntypes), and including to other ABCs.\n\nclass.__instancecheck__(self, instance)\n\n Return true if *instance* should be considered a (direct or\n indirect) instance of *class*. If defined, called to implement\n ``isinstance(instance, class)``.\n\nclass.__subclasscheck__(self, subclass)\n\n Return true if *subclass* should be considered a (direct or\n indirect) subclass of *class*. If defined, called to implement\n ``issubclass(subclass, class)``.\n\nNote that these methods are looked up on the type (metaclass) of a\nclass. They cannot be defined as class methods in the actual class.\nThis is consistent with the lookup of special methods that are called\non instances, only that in this case the instance is itself a class.\n\nSee also:\n\n **PEP 3119** - Introducing Abstract Base Classes\n Includes the specification for customizing ``isinstance()`` and\n ``issubclass()`` behavior through ``__instancecheck__()`` and\n ``__subclasscheck__()``, with motivation for this functionality\n in the context of adding Abstract Base Classes (see the ``abc``\n module) to the language.\n\n\nEmulating callable objects\n==========================\n\nobject.__call__(self[, args...])\n\n Called when the instance is "called" as a function; if this method\n is defined, ``x(arg1, arg2, ...)`` is a shorthand for\n ``x.__call__(arg1, arg2, ...)``.\n\n\nEmulating container types\n=========================\n\nThe following methods can be defined to implement container objects.\nContainers usually are sequences (such as lists or tuples) or mappings\n(like dictionaries), but can represent other containers as well. The\nfirst set of methods is used either to emulate a sequence or to\nemulate a mapping; the difference is that for a sequence, the\nallowable keys should be the integers *k* for which ``0 <= k < N``\nwhere *N* is the length of the sequence, or slice objects, which\ndefine a range of items. (For backwards compatibility, the method\n``__getslice__()`` (see below) can also be defined to handle simple,\nbut not extended slices.) It is also recommended that mappings provide\nthe methods ``keys()``, ``values()``, ``items()``, ``has_key()``,\n``get()``, ``clear()``, ``setdefault()``, ``iterkeys()``,\n``itervalues()``, ``iteritems()``, ``pop()``, ``popitem()``,\n``copy()``, and ``update()`` behaving similar to those for Python\'s\nstandard dictionary objects. The ``UserDict`` module provides a\n``DictMixin`` class to help create those methods from a base set of\n``__getitem__()``, ``__setitem__()``, ``__delitem__()``, and\n``keys()``. Mutable sequences should provide methods ``append()``,\n``count()``, ``index()``, ``extend()``, ``insert()``, ``pop()``,\n``remove()``, ``reverse()`` and ``sort()``, like Python standard list\nobjects. Finally, sequence types should implement addition (meaning\nconcatenation) and multiplication (meaning repetition) by defining the\nmethods ``__add__()``, ``__radd__()``, ``__iadd__()``, ``__mul__()``,\n``__rmul__()`` and ``__imul__()`` described below; they should not\ndefine ``__coerce__()`` or other numerical operators. It is\nrecommended that both mappings and sequences implement the\n``__contains__()`` method to allow efficient use of the ``in``\noperator; for mappings, ``in`` should be equivalent of ``has_key()``;\nfor sequences, it should search through the values. It is further\nrecommended that both mappings and sequences implement the\n``__iter__()`` method to allow efficient iteration through the\ncontainer; for mappings, ``__iter__()`` should be the same as\n``iterkeys()``; for sequences, it should iterate through the values.\n\nobject.__len__(self)\n\n Called to implement the built-in function ``len()``. Should return\n the length of the object, an integer ``>=`` 0. Also, an object\n that doesn\'t define a ``__nonzero__()`` method and whose\n ``__len__()`` method returns zero is considered to be false in a\n Boolean context.\n\nobject.__getitem__(self, key)\n\n Called to implement evaluation of ``self[key]``. For sequence\n types, the accepted keys should be integers and slice objects.\n Note that the special interpretation of negative indexes (if the\n class wishes to emulate a sequence type) is up to the\n ``__getitem__()`` method. If *key* is of an inappropriate type,\n ``TypeError`` may be raised; if of a value outside the set of\n indexes for the sequence (after any special interpretation of\n negative values), ``IndexError`` should be raised. For mapping\n types, if *key* is missing (not in the container), ``KeyError``\n should be raised.\n\n Note: ``for`` loops expect that an ``IndexError`` will be raised for\n illegal indexes to allow proper detection of the end of the\n sequence.\n\nobject.__setitem__(self, key, value)\n\n Called to implement assignment to ``self[key]``. Same note as for\n ``__getitem__()``. This should only be implemented for mappings if\n the objects support changes to the values for keys, or if new keys\n can be added, or for sequences if elements can be replaced. The\n same exceptions should be raised for improper *key* values as for\n the ``__getitem__()`` method.\n\nobject.__delitem__(self, key)\n\n Called to implement deletion of ``self[key]``. Same note as for\n ``__getitem__()``. This should only be implemented for mappings if\n the objects support removal of keys, or for sequences if elements\n can be removed from the sequence. The same exceptions should be\n raised for improper *key* values as for the ``__getitem__()``\n method.\n\nobject.__iter__(self)\n\n This method is called when an iterator is required for a container.\n This method should return a new iterator object that can iterate\n over all the objects in the container. For mappings, it should\n iterate over the keys of the container, and should also be made\n available as the method ``iterkeys()``.\n\n Iterator objects also need to implement this method; they are\n required to return themselves. For more information on iterator\n objects, see *Iterator Types*.\n\nobject.__reversed__(self)\n\n Called (if present) by the ``reversed()`` built-in to implement\n reverse iteration. It should return a new iterator object that\n iterates over all the objects in the container in reverse order.\n\n If the ``__reversed__()`` method is not provided, the\n ``reversed()`` built-in will fall back to using the sequence\n protocol (``__len__()`` and ``__getitem__()``). Objects that\n support the sequence protocol should only provide\n ``__reversed__()`` if they can provide an implementation that is\n more efficient than the one provided by ``reversed()``.\n\n New in version 2.6.\n\nThe membership test operators (``in`` and ``not in``) are normally\nimplemented as an iteration through a sequence. However, container\nobjects can supply the following special method with a more efficient\nimplementation, which also does not require the object be a sequence.\n\nobject.__contains__(self, item)\n\n Called to implement membership test operators. Should return true\n if *item* is in *self*, false otherwise. For mapping objects, this\n should consider the keys of the mapping rather than the values or\n the key-item pairs.\n\n For objects that don\'t define ``__contains__()``, the membership\n test first tries iteration via ``__iter__()``, then the old\n sequence iteration protocol via ``__getitem__()``, see *this\n section in the language reference*.\n\n\nAdditional methods for emulation of sequence types\n==================================================\n\nThe following optional methods can be defined to further emulate\nsequence objects. Immutable sequences methods should at most only\ndefine ``__getslice__()``; mutable sequences might define all three\nmethods.\n\nobject.__getslice__(self, i, j)\n\n Deprecated since version 2.0: Support slice objects as parameters\n to the ``__getitem__()`` method. (However, built-in types in\n CPython currently still implement ``__getslice__()``. Therefore,\n you have to override it in derived classes when implementing\n slicing.)\n\n Called to implement evaluation of ``self[i:j]``. The returned\n object should be of the same type as *self*. Note that missing *i*\n or *j* in the slice expression are replaced by zero or\n ``sys.maxint``, respectively. If negative indexes are used in the\n slice, the length of the sequence is added to that index. If the\n instance does not implement the ``__len__()`` method, an\n ``AttributeError`` is raised. No guarantee is made that indexes\n adjusted this way are not still negative. Indexes which are\n greater than the length of the sequence are not modified. If no\n ``__getslice__()`` is found, a slice object is created instead, and\n passed to ``__getitem__()`` instead.\n\nobject.__setslice__(self, i, j, sequence)\n\n Called to implement assignment to ``self[i:j]``. Same notes for *i*\n and *j* as for ``__getslice__()``.\n\n This method is deprecated. If no ``__setslice__()`` is found, or\n for extended slicing of the form ``self[i:j:k]``, a slice object is\n created, and passed to ``__setitem__()``, instead of\n ``__setslice__()`` being called.\n\nobject.__delslice__(self, i, j)\n\n Called to implement deletion of ``self[i:j]``. Same notes for *i*\n and *j* as for ``__getslice__()``. This method is deprecated. If no\n ``__delslice__()`` is found, or for extended slicing of the form\n ``self[i:j:k]``, a slice object is created, and passed to\n ``__delitem__()``, instead of ``__delslice__()`` being called.\n\nNotice that these methods are only invoked when a single slice with a\nsingle colon is used, and the slice method is available. For slice\noperations involving extended slice notation, or in absence of the\nslice methods, ``__getitem__()``, ``__setitem__()`` or\n``__delitem__()`` is called with a slice object as argument.\n\nThe following example demonstrate how to make your program or module\ncompatible with earlier versions of Python (assuming that methods\n``__getitem__()``, ``__setitem__()`` and ``__delitem__()`` support\nslice objects as arguments):\n\n class MyClass:\n ...\n def __getitem__(self, index):\n ...\n def __setitem__(self, index, value):\n ...\n def __delitem__(self, index):\n ...\n\n if sys.version_info < (2, 0):\n # They won\'t be defined if version is at least 2.0 final\n\n def __getslice__(self, i, j):\n return self[max(0, i):max(0, j):]\n def __setslice__(self, i, j, seq):\n self[max(0, i):max(0, j):] = seq\n def __delslice__(self, i, j):\n del self[max(0, i):max(0, j):]\n ...\n\nNote the calls to ``max()``; these are necessary because of the\nhandling of negative indices before the ``__*slice__()`` methods are\ncalled. When negative indexes are used, the ``__*item__()`` methods\nreceive them as provided, but the ``__*slice__()`` methods get a\n"cooked" form of the index values. For each negative index value, the\nlength of the sequence is added to the index before calling the method\n(which may still result in a negative index); this is the customary\nhandling of negative indexes by the built-in sequence types, and the\n``__*item__()`` methods are expected to do this as well. However,\nsince they should already be doing that, negative indexes cannot be\npassed in; they must be constrained to the bounds of the sequence\nbefore being passed to the ``__*item__()`` methods. Calling ``max(0,\ni)`` conveniently returns the proper value.\n\n\nEmulating numeric types\n=======================\n\nThe following methods can be defined to emulate numeric objects.\nMethods corresponding to operations that are not supported by the\nparticular kind of number implemented (e.g., bitwise operations for\nnon-integral numbers) should be left undefined.\n\nobject.__add__(self, other)\nobject.__sub__(self, other)\nobject.__mul__(self, other)\nobject.__floordiv__(self, other)\nobject.__mod__(self, other)\nobject.__divmod__(self, other)\nobject.__pow__(self, other[, modulo])\nobject.__lshift__(self, other)\nobject.__rshift__(self, other)\nobject.__and__(self, other)\nobject.__xor__(self, other)\nobject.__or__(self, other)\n\n These methods are called to implement the binary arithmetic\n operations (``+``, ``-``, ``*``, ``//``, ``%``, ``divmod()``,\n ``pow()``, ``**``, ``<<``, ``>>``, ``&``, ``^``, ``|``). For\n instance, to evaluate the expression ``x + y``, where *x* is an\n instance of a class that has an ``__add__()`` method,\n ``x.__add__(y)`` is called. The ``__divmod__()`` method should be\n the equivalent to using ``__floordiv__()`` and ``__mod__()``; it\n should not be related to ``__truediv__()`` (described below). Note\n that ``__pow__()`` should be defined to accept an optional third\n argument if the ternary version of the built-in ``pow()`` function\n is to be supported.\n\n If one of those methods does not support the operation with the\n supplied arguments, it should return ``NotImplemented``.\n\nobject.__div__(self, other)\nobject.__truediv__(self, other)\n\n The division operator (``/``) is implemented by these methods. The\n ``__truediv__()`` method is used when ``__future__.division`` is in\n effect, otherwise ``__div__()`` is used. If only one of these two\n methods is defined, the object will not support division in the\n alternate context; ``TypeError`` will be raised instead.\n\nobject.__radd__(self, other)\nobject.__rsub__(self, other)\nobject.__rmul__(self, other)\nobject.__rdiv__(self, other)\nobject.__rtruediv__(self, other)\nobject.__rfloordiv__(self, other)\nobject.__rmod__(self, other)\nobject.__rdivmod__(self, other)\nobject.__rpow__(self, other)\nobject.__rlshift__(self, other)\nobject.__rrshift__(self, other)\nobject.__rand__(self, other)\nobject.__rxor__(self, other)\nobject.__ror__(self, other)\n\n These methods are called to implement the binary arithmetic\n operations (``+``, ``-``, ``*``, ``/``, ``%``, ``divmod()``,\n ``pow()``, ``**``, ``<<``, ``>>``, ``&``, ``^``, ``|``) with\n reflected (swapped) operands. These functions are only called if\n the left operand does not support the corresponding operation and\n the operands are of different types. [2] For instance, to evaluate\n the expression ``x - y``, where *y* is an instance of a class that\n has an ``__rsub__()`` method, ``y.__rsub__(x)`` is called if\n ``x.__sub__(y)`` returns *NotImplemented*.\n\n Note that ternary ``pow()`` will not try calling ``__rpow__()``\n (the coercion rules would become too complicated).\n\n Note: If the right operand\'s type is a subclass of the left operand\'s\n type and that subclass provides the reflected method for the\n operation, this method will be called before the left operand\'s\n non-reflected method. This behavior allows subclasses to\n override their ancestors\' operations.\n\nobject.__iadd__(self, other)\nobject.__isub__(self, other)\nobject.__imul__(self, other)\nobject.__idiv__(self, other)\nobject.__itruediv__(self, other)\nobject.__ifloordiv__(self, other)\nobject.__imod__(self, other)\nobject.__ipow__(self, other[, modulo])\nobject.__ilshift__(self, other)\nobject.__irshift__(self, other)\nobject.__iand__(self, other)\nobject.__ixor__(self, other)\nobject.__ior__(self, other)\n\n These methods are called to implement the augmented arithmetic\n assignments (``+=``, ``-=``, ``*=``, ``/=``, ``//=``, ``%=``,\n ``**=``, ``<<=``, ``>>=``, ``&=``, ``^=``, ``|=``). These methods\n should attempt to do the operation in-place (modifying *self*) and\n return the result (which could be, but does not have to be,\n *self*). If a specific method is not defined, the augmented\n assignment falls back to the normal methods. For instance, to\n execute the statement ``x += y``, where *x* is an instance of a\n class that has an ``__iadd__()`` method, ``x.__iadd__(y)`` is\n called. If *x* is an instance of a class that does not define a\n ``__iadd__()`` method, ``x.__add__(y)`` and ``y.__radd__(x)`` are\n considered, as with the evaluation of ``x + y``.\n\nobject.__neg__(self)\nobject.__pos__(self)\nobject.__abs__(self)\nobject.__invert__(self)\n\n Called to implement the unary arithmetic operations (``-``, ``+``,\n ``abs()`` and ``~``).\n\nobject.__complex__(self)\nobject.__int__(self)\nobject.__long__(self)\nobject.__float__(self)\n\n Called to implement the built-in functions ``complex()``,\n ``int()``, ``long()``, and ``float()``. Should return a value of\n the appropriate type.\n\nobject.__oct__(self)\nobject.__hex__(self)\n\n Called to implement the built-in functions ``oct()`` and ``hex()``.\n Should return a string value.\n\nobject.__index__(self)\n\n Called to implement ``operator.index()``. Also called whenever\n Python needs an integer object (such as in slicing). Must return\n an integer (int or long).\n\n New in version 2.5.\n\nobject.__coerce__(self, other)\n\n Called to implement "mixed-mode" numeric arithmetic. Should either\n return a 2-tuple containing *self* and *other* converted to a\n common numeric type, or ``None`` if conversion is impossible. When\n the common type would be the type of ``other``, it is sufficient to\n return ``None``, since the interpreter will also ask the other\n object to attempt a coercion (but sometimes, if the implementation\n of the other type cannot be changed, it is useful to do the\n conversion to the other type here). A return value of\n ``NotImplemented`` is equivalent to returning ``None``.\n\n\nCoercion rules\n==============\n\nThis section used to document the rules for coercion. As the language\nhas evolved, the coercion rules have become hard to document\nprecisely; documenting what one version of one particular\nimplementation does is undesirable. Instead, here are some informal\nguidelines regarding coercion. In Python 3.0, coercion will not be\nsupported.\n\n* If the left operand of a % operator is a string or Unicode object,\n no coercion takes place and the string formatting operation is\n invoked instead.\n\n* It is no longer recommended to define a coercion operation. Mixed-\n mode operations on types that don\'t define coercion pass the\n original arguments to the operation.\n\n* New-style classes (those derived from ``object``) never invoke the\n ``__coerce__()`` method in response to a binary operator; the only\n time ``__coerce__()`` is invoked is when the built-in function\n ``coerce()`` is called.\n\n* For most intents and purposes, an operator that returns\n ``NotImplemented`` is treated the same as one that is not\n implemented at all.\n\n* Below, ``__op__()`` and ``__rop__()`` are used to signify the\n generic method names corresponding to an operator; ``__iop__()`` is\n used for the corresponding in-place operator. For example, for the\n operator \'``+``\', ``__add__()`` and ``__radd__()`` are used for the\n left and right variant of the binary operator, and ``__iadd__()``\n for the in-place variant.\n\n* For objects *x* and *y*, first ``x.__op__(y)`` is tried. If this is\n not implemented or returns ``NotImplemented``, ``y.__rop__(x)`` is\n tried. If this is also not implemented or returns\n ``NotImplemented``, a ``TypeError`` exception is raised. But see\n the following exception:\n\n* Exception to the previous item: if the left operand is an instance\n of a built-in type or a new-style class, and the right operand is an\n instance of a proper subclass of that type or class and overrides\n the base\'s ``__rop__()`` method, the right operand\'s ``__rop__()``\n method is tried *before* the left operand\'s ``__op__()`` method.\n\n This is done so that a subclass can completely override binary\n operators. Otherwise, the left operand\'s ``__op__()`` method would\n always accept the right operand: when an instance of a given class\n is expected, an instance of a subclass of that class is always\n acceptable.\n\n* When either operand type defines a coercion, this coercion is called\n before that type\'s ``__op__()`` or ``__rop__()`` method is called,\n but no sooner. If the coercion returns an object of a different\n type for the operand whose coercion is invoked, part of the process\n is redone using the new object.\n\n* When an in-place operator (like \'``+=``\') is used, if the left\n operand implements ``__iop__()``, it is invoked without any\n coercion. When the operation falls back to ``__op__()`` and/or\n ``__rop__()``, the normal coercion rules apply.\n\n* In ``x + y``, if *x* is a sequence that implements sequence\n concatenation, sequence concatenation is invoked.\n\n* In ``x * y``, if one operator is a sequence that implements sequence\n repetition, and the other is an integer (``int`` or ``long``),\n sequence repetition is invoked.\n\n* Rich comparisons (implemented by methods ``__eq__()`` and so on)\n never use coercion. Three-way comparison (implemented by\n ``__cmp__()``) does use coercion under the same conditions as other\n binary operations use it.\n\n* In the current implementation, the built-in numeric types ``int``,\n ``long`` and ``float`` do not use coercion; the type ``complex``\n however does use coercion for binary operators and rich comparisons,\n despite the above rules. The difference can become apparent when\n subclassing these types. Over time, the type ``complex`` may be\n fixed to avoid coercion. All these types implement a\n ``__coerce__()`` method, for use by the built-in ``coerce()``\n function.\n\n\nWith Statement Context Managers\n===============================\n\nNew in version 2.5.\n\nA *context manager* is an object that defines the runtime context to\nbe established when executing a ``with`` statement. The context\nmanager handles the entry into, and the exit from, the desired runtime\ncontext for the execution of the block of code. Context managers are\nnormally invoked using the ``with`` statement (described in section\n*The with statement*), but can also be used by directly invoking their\nmethods.\n\nTypical uses of context managers include saving and restoring various\nkinds of global state, locking and unlocking resources, closing opened\nfiles, etc.\n\nFor more information on context managers, see *Context Manager Types*.\n\nobject.__enter__(self)\n\n Enter the runtime context related to this object. The ``with``\n statement will bind this method\'s return value to the target(s)\n specified in the ``as`` clause of the statement, if any.\n\nobject.__exit__(self, exc_type, exc_value, traceback)\n\n Exit the runtime context related to this object. The parameters\n describe the exception that caused the context to be exited. If the\n context was exited without an exception, all three arguments will\n be ``None``.\n\n If an exception is supplied, and the method wishes to suppress the\n exception (i.e., prevent it from being propagated), it should\n return a true value. Otherwise, the exception will be processed\n normally upon exit from this method.\n\n Note that ``__exit__()`` methods should not reraise the passed-in\n exception; this is the caller\'s responsibility.\n\nSee also:\n\n **PEP 0343** - The "with" statement\n The specification, background, and examples for the Python\n ``with`` statement.\n\n\nSpecial method lookup for old-style classes\n===========================================\n\nFor old-style classes, special methods are always looked up in exactly\nthe same way as any other method or attribute. This is the case\nregardless of whether the method is being looked up explicitly as in\n``x.__getitem__(i)`` or implicitly as in ``x[i]``.\n\nThis behaviour means that special methods may exhibit different\nbehaviour for different instances of a single old-style class if the\nappropriate special attributes are set differently:\n\n >>> class C:\n ... pass\n ...\n >>> c1 = C()\n >>> c2 = C()\n >>> c1.__len__ = lambda: 5\n >>> c2.__len__ = lambda: 9\n >>> len(c1)\n 5\n >>> len(c2)\n 9\n\n\nSpecial method lookup for new-style classes\n===========================================\n\nFor new-style classes, implicit invocations of special methods are\nonly guaranteed to work correctly if defined on an object\'s type, not\nin the object\'s instance dictionary. That behaviour is the reason why\nthe following code raises an exception (unlike the equivalent example\nwith old-style classes):\n\n >>> class C(object):\n ... pass\n ...\n >>> c = C()\n >>> c.__len__ = lambda: 5\n >>> len(c)\n Traceback (most recent call last):\n File "", line 1, in \n TypeError: object of type \'C\' has no len()\n\nThe rationale behind this behaviour lies with a number of special\nmethods such as ``__hash__()`` and ``__repr__()`` that are implemented\nby all objects, including type objects. If the implicit lookup of\nthese methods used the conventional lookup process, they would fail\nwhen invoked on the type object itself:\n\n >>> 1 .__hash__() == hash(1)\n True\n >>> int.__hash__() == hash(int)\n Traceback (most recent call last):\n File "", line 1, in \n TypeError: descriptor \'__hash__\' of \'int\' object needs an argument\n\nIncorrectly attempting to invoke an unbound method of a class in this\nway is sometimes referred to as \'metaclass confusion\', and is avoided\nby bypassing the instance when looking up special methods:\n\n >>> type(1).__hash__(1) == hash(1)\n True\n >>> type(int).__hash__(int) == hash(int)\n True\n\nIn addition to bypassing any instance attributes in the interest of\ncorrectness, implicit special method lookup generally also bypasses\nthe ``__getattribute__()`` method even of the object\'s metaclass:\n\n >>> class Meta(type):\n ... def __getattribute__(*args):\n ... print "Metaclass getattribute invoked"\n ... return type.__getattribute__(*args)\n ...\n >>> class C(object):\n ... __metaclass__ = Meta\n ... def __len__(self):\n ... return 10\n ... def __getattribute__(*args):\n ... print "Class getattribute invoked"\n ... return object.__getattribute__(*args)\n ...\n >>> c = C()\n >>> c.__len__() # Explicit lookup via instance\n Class getattribute invoked\n 10\n >>> type(c).__len__(c) # Explicit lookup via type\n Metaclass getattribute invoked\n 10\n >>> len(c) # Implicit lookup\n 10\n\nBypassing the ``__getattribute__()`` machinery in this fashion\nprovides significant scope for speed optimisations within the\ninterpreter, at the cost of some flexibility in the handling of\nspecial methods (the special method *must* be set on the class object\nitself in order to be consistently invoked by the interpreter).\n\n-[ Footnotes ]-\n\n[1] It *is* possible in some cases to change an object\'s type, under\n certain controlled conditions. It generally isn\'t a good idea\n though, since it can lead to some very strange behaviour if it is\n handled incorrectly.\n\n[2] For operands of the same type, it is assumed that if the non-\n reflected method (such as ``__add__()``) fails the operation is\n not supported, which is why the reflected method is not called.\n', 'string-conversions': u'\nString conversions\n******************\n\nA string conversion is an expression list enclosed in reverse (a.k.a.\nbackward) quotes:\n\n string_conversion ::= "\'" expression_list "\'"\n\nA string conversion evaluates the contained expression list and\nconverts the resulting object into a string according to rules\nspecific to its type.\n\nIf the object is a string, a number, ``None``, or a tuple, list or\ndictionary containing only objects whose type is one of these, the\nresulting string is a valid Python expression which can be passed to\nthe built-in function ``eval()`` to yield an expression with the same\nvalue (or an approximation, if floating point numbers are involved).\n\n(In particular, converting a string adds quotes around it and converts\n"funny" characters to escape sequences that are safe to print.)\n\nRecursive objects (for example, lists or dictionaries that contain a\nreference to themselves, directly or indirectly) use ``...`` to\nindicate a recursive reference, and the result cannot be passed to\n``eval()`` to get an equal value (``SyntaxError`` will be raised\ninstead).\n\nThe built-in function ``repr()`` performs exactly the same conversion\nin its argument as enclosing it in parentheses and reverse quotes\ndoes. The built-in function ``str()`` performs a similar but more\nuser-friendly conversion.\n', - 'string-methods': u'\nString Methods\n**************\n\nBelow are listed the string methods which both 8-bit strings and\nUnicode objects support. Note that none of these methods take keyword\narguments.\n\nIn addition, Python\'s strings support the sequence type methods\ndescribed in the *Sequence Types --- str, unicode, list, tuple,\nbuffer, xrange* section. To output formatted strings use template\nstrings or the ``%`` operator described in the *String Formatting\nOperations* section. Also, see the ``re`` module for string functions\nbased on regular expressions.\n\nstr.capitalize()\n\n Return a copy of the string with only its first character\n capitalized.\n\n For 8-bit strings, this method is locale-dependent.\n\nstr.center(width[, fillchar])\n\n Return centered in a string of length *width*. Padding is done\n using the specified *fillchar* (default is a space).\n\n Changed in version 2.4: Support for the *fillchar* argument.\n\nstr.count(sub[, start[, end]])\n\n Return the number of non-overlapping occurrences of substring *sub*\n in the range [*start*, *end*]. Optional arguments *start* and\n *end* are interpreted as in slice notation.\n\nstr.decode([encoding[, errors]])\n\n Decodes the string using the codec registered for *encoding*.\n *encoding* defaults to the default string encoding. *errors* may\n be given to set a different error handling scheme. The default is\n ``\'strict\'``, meaning that encoding errors raise ``UnicodeError``.\n Other possible values are ``\'ignore\'``, ``\'replace\'`` and any other\n name registered via ``codecs.register_error()``, see section *Codec\n Base Classes*.\n\n New in version 2.2.\n\n Changed in version 2.3: Support for other error handling schemes\n added.\n\nstr.encode([encoding[, errors]])\n\n Return an encoded version of the string. Default encoding is the\n current default string encoding. *errors* may be given to set a\n different error handling scheme. The default for *errors* is\n ``\'strict\'``, meaning that encoding errors raise a\n ``UnicodeError``. Other possible values are ``\'ignore\'``,\n ``\'replace\'``, ``\'xmlcharrefreplace\'``, ``\'backslashreplace\'`` and\n any other name registered via ``codecs.register_error()``, see\n section *Codec Base Classes*. For a list of possible encodings, see\n section *Standard Encodings*.\n\n New in version 2.0.\n\n Changed in version 2.3: Support for ``\'xmlcharrefreplace\'`` and\n ``\'backslashreplace\'`` and other error handling schemes added.\n\nstr.endswith(suffix[, start[, end]])\n\n Return ``True`` if the string ends with the specified *suffix*,\n otherwise return ``False``. *suffix* can also be a tuple of\n suffixes to look for. With optional *start*, test beginning at\n that position. With optional *end*, stop comparing at that\n position.\n\n Changed in version 2.5: Accept tuples as *suffix*.\n\nstr.expandtabs([tabsize])\n\n Return a copy of the string where all tab characters are replaced\n by one or more spaces, depending on the current column and the\n given tab size. The column number is reset to zero after each\n newline occurring in the string. If *tabsize* is not given, a tab\n size of ``8`` characters is assumed. This doesn\'t understand other\n non-printing characters or escape sequences.\n\nstr.find(sub[, start[, end]])\n\n Return the lowest index in the string where substring *sub* is\n found, such that *sub* is contained in the range [*start*, *end*].\n Optional arguments *start* and *end* are interpreted as in slice\n notation. Return ``-1`` if *sub* is not found.\n\nstr.format(*args, **kwargs)\n\n Perform a string formatting operation. The *format_string*\n argument can contain literal text or replacement fields delimited\n by braces ``{}``. Each replacement field contains either the\n numeric index of a positional argument, or the name of a keyword\n argument. Returns a copy of *format_string* where each replacement\n field is replaced with the string value of the corresponding\n argument.\n\n >>> "The sum of 1 + 2 is {0}".format(1+2)\n \'The sum of 1 + 2 is 3\'\n\n See *Format String Syntax* for a description of the various\n formatting options that can be specified in format strings.\n\n This method of string formatting is the new standard in Python 3.0,\n and should be preferred to the ``%`` formatting described in\n *String Formatting Operations* in new code.\n\n New in version 2.6.\n\nstr.index(sub[, start[, end]])\n\n Like ``find()``, but raise ``ValueError`` when the substring is not\n found.\n\nstr.isalnum()\n\n Return true if all characters in the string are alphanumeric and\n there is at least one character, false otherwise.\n\n For 8-bit strings, this method is locale-dependent.\n\nstr.isalpha()\n\n Return true if all characters in the string are alphabetic and\n there is at least one character, false otherwise.\n\n For 8-bit strings, this method is locale-dependent.\n\nstr.isdigit()\n\n Return true if all characters in the string are digits and there is\n at least one character, false otherwise.\n\n For 8-bit strings, this method is locale-dependent.\n\nstr.islower()\n\n Return true if all cased characters in the string are lowercase and\n there is at least one cased character, false otherwise.\n\n For 8-bit strings, this method is locale-dependent.\n\nstr.isspace()\n\n Return true if there are only whitespace characters in the string\n and there is at least one character, false otherwise.\n\n For 8-bit strings, this method is locale-dependent.\n\nstr.istitle()\n\n Return true if the string is a titlecased string and there is at\n least one character, for example uppercase characters may only\n follow uncased characters and lowercase characters only cased ones.\n Return false otherwise.\n\n For 8-bit strings, this method is locale-dependent.\n\nstr.isupper()\n\n Return true if all cased characters in the string are uppercase and\n there is at least one cased character, false otherwise.\n\n For 8-bit strings, this method is locale-dependent.\n\nstr.join(iterable)\n\n Return a string which is the concatenation of the strings in the\n *iterable* *iterable*. The separator between elements is the\n string providing this method.\n\nstr.ljust(width[, fillchar])\n\n Return the string left justified in a string of length *width*.\n Padding is done using the specified *fillchar* (default is a\n space). The original string is returned if *width* is less than\n ``len(s)``.\n\n Changed in version 2.4: Support for the *fillchar* argument.\n\nstr.lower()\n\n Return a copy of the string converted to lowercase.\n\n For 8-bit strings, this method is locale-dependent.\n\nstr.lstrip([chars])\n\n Return a copy of the string with leading characters removed. The\n *chars* argument is a string specifying the set of characters to be\n removed. If omitted or ``None``, the *chars* argument defaults to\n removing whitespace. The *chars* argument is not a prefix; rather,\n all combinations of its values are stripped:\n\n >>> \' spacious \'.lstrip()\n \'spacious \'\n >>> \'www.example.com\'.lstrip(\'cmowz.\')\n \'example.com\'\n\n Changed in version 2.2.2: Support for the *chars* argument.\n\nstr.partition(sep)\n\n Split the string at the first occurrence of *sep*, and return a\n 3-tuple containing the part before the separator, the separator\n itself, and the part after the separator. If the separator is not\n found, return a 3-tuple containing the string itself, followed by\n two empty strings.\n\n New in version 2.5.\n\nstr.replace(old, new[, count])\n\n Return a copy of the string with all occurrences of substring *old*\n replaced by *new*. If the optional argument *count* is given, only\n the first *count* occurrences are replaced.\n\nstr.rfind(sub[, start[, end]])\n\n Return the highest index in the string where substring *sub* is\n found, such that *sub* is contained within s[start,end]. Optional\n arguments *start* and *end* are interpreted as in slice notation.\n Return ``-1`` on failure.\n\nstr.rindex(sub[, start[, end]])\n\n Like ``rfind()`` but raises ``ValueError`` when the substring *sub*\n is not found.\n\nstr.rjust(width[, fillchar])\n\n Return the string right justified in a string of length *width*.\n Padding is done using the specified *fillchar* (default is a\n space). The original string is returned if *width* is less than\n ``len(s)``.\n\n Changed in version 2.4: Support for the *fillchar* argument.\n\nstr.rpartition(sep)\n\n Split the string at the last occurrence of *sep*, and return a\n 3-tuple containing the part before the separator, the separator\n itself, and the part after the separator. If the separator is not\n found, return a 3-tuple containing two empty strings, followed by\n the string itself.\n\n New in version 2.5.\n\nstr.rsplit([sep[, maxsplit]])\n\n Return a list of the words in the string, using *sep* as the\n delimiter string. If *maxsplit* is given, at most *maxsplit* splits\n are done, the *rightmost* ones. If *sep* is not specified or\n ``None``, any whitespace string is a separator. Except for\n splitting from the right, ``rsplit()`` behaves like ``split()``\n which is described in detail below.\n\n New in version 2.4.\n\nstr.rstrip([chars])\n\n Return a copy of the string with trailing characters removed. The\n *chars* argument is a string specifying the set of characters to be\n removed. If omitted or ``None``, the *chars* argument defaults to\n removing whitespace. The *chars* argument is not a suffix; rather,\n all combinations of its values are stripped:\n\n >>> \' spacious \'.rstrip()\n \' spacious\'\n >>> \'mississippi\'.rstrip(\'ipz\')\n \'mississ\'\n\n Changed in version 2.2.2: Support for the *chars* argument.\n\nstr.split([sep[, maxsplit]])\n\n Return a list of the words in the string, using *sep* as the\n delimiter string. If *maxsplit* is given, at most *maxsplit*\n splits are done (thus, the list will have at most ``maxsplit+1``\n elements). If *maxsplit* is not specified, then there is no limit\n on the number of splits (all possible splits are made).\n\n If *sep* is given, consecutive delimiters are not grouped together\n and are deemed to delimit empty strings (for example,\n ``\'1,,2\'.split(\',\')`` returns ``[\'1\', \'\', \'2\']``). The *sep*\n argument may consist of multiple characters (for example,\n ``\'1<>2<>3\'.split(\'<>\')`` returns ``[\'1\', \'2\', \'3\']``). Splitting\n an empty string with a specified separator returns ``[\'\']``.\n\n If *sep* is not specified or is ``None``, a different splitting\n algorithm is applied: runs of consecutive whitespace are regarded\n as a single separator, and the result will contain no empty strings\n at the start or end if the string has leading or trailing\n whitespace. Consequently, splitting an empty string or a string\n consisting of just whitespace with a ``None`` separator returns\n ``[]``.\n\n For example, ``\' 1 2 3 \'.split()`` returns ``[\'1\', \'2\', \'3\']``,\n and ``\' 1 2 3 \'.split(None, 1)`` returns ``[\'1\', \'2 3 \']``.\n\nstr.splitlines([keepends])\n\n Return a list of the lines in the string, breaking at line\n boundaries. Line breaks are not included in the resulting list\n unless *keepends* is given and true.\n\nstr.startswith(prefix[, start[, end]])\n\n Return ``True`` if string starts with the *prefix*, otherwise\n return ``False``. *prefix* can also be a tuple of prefixes to look\n for. With optional *start*, test string beginning at that\n position. With optional *end*, stop comparing string at that\n position.\n\n Changed in version 2.5: Accept tuples as *prefix*.\n\nstr.strip([chars])\n\n Return a copy of the string with the leading and trailing\n characters removed. The *chars* argument is a string specifying the\n set of characters to be removed. If omitted or ``None``, the\n *chars* argument defaults to removing whitespace. The *chars*\n argument is not a prefix or suffix; rather, all combinations of its\n values are stripped:\n\n >>> \' spacious \'.strip()\n \'spacious\'\n >>> \'www.example.com\'.strip(\'cmowz.\')\n \'example\'\n\n Changed in version 2.2.2: Support for the *chars* argument.\n\nstr.swapcase()\n\n Return a copy of the string with uppercase characters converted to\n lowercase and vice versa.\n\n For 8-bit strings, this method is locale-dependent.\n\nstr.title()\n\n Return a titlecased version of the string where words start with an\n uppercase character and the remaining characters are lowercase.\n\n The algorithm uses a simple language-independent definition of a\n word as groups of consecutive letters. The definition works in\n many contexts but it means that apostrophes in contractions and\n possessives form word boundaries, which may not be the desired\n result:\n\n >>> "they\'re bill\'s friends from the UK".title()\n "They\'Re Bill\'S Friends From The Uk"\n\n A workaround for apostrophes can be constructed using regular\n expressions:\n\n >>> import re\n >>> def titlecase(s):\n return re.sub(r"[A-Za-z]+(\'[A-Za-z]+)?",\n lambda mo: mo.group(0)[0].upper() +\n mo.group(0)[1:].lower(),\n s)\n\n >>> titlecase("they\'re bill\'s friends.")\n "They\'re Bill\'s Friends."\n\n For 8-bit strings, this method is locale-dependent.\n\nstr.translate(table[, deletechars])\n\n Return a copy of the string where all characters occurring in the\n optional argument *deletechars* are removed, and the remaining\n characters have been mapped through the given translation table,\n which must be a string of length 256.\n\n You can use the ``maketrans()`` helper function in the ``string``\n module to create a translation table. For string objects, set the\n *table* argument to ``None`` for translations that only delete\n characters:\n\n >>> \'read this short text\'.translate(None, \'aeiou\')\n \'rd ths shrt txt\'\n\n New in version 2.6: Support for a ``None`` *table* argument.\n\n For Unicode objects, the ``translate()`` method does not accept the\n optional *deletechars* argument. Instead, it returns a copy of the\n *s* where all characters have been mapped through the given\n translation table which must be a mapping of Unicode ordinals to\n Unicode ordinals, Unicode strings or ``None``. Unmapped characters\n are left untouched. Characters mapped to ``None`` are deleted.\n Note, a more flexible approach is to create a custom character\n mapping codec using the ``codecs`` module (see ``encodings.cp1251``\n for an example).\n\nstr.upper()\n\n Return a copy of the string converted to uppercase.\n\n For 8-bit strings, this method is locale-dependent.\n\nstr.zfill(width)\n\n Return the numeric string left filled with zeros in a string of\n length *width*. A sign prefix is handled correctly. The original\n string is returned if *width* is less than ``len(s)``.\n\n New in version 2.2.2.\n\nThe following methods are present only on unicode objects:\n\nunicode.isnumeric()\n\n Return ``True`` if there are only numeric characters in S,\n ``False`` otherwise. Numeric characters include digit characters,\n and all characters that have the Unicode numeric value property,\n e.g. U+2155, VULGAR FRACTION ONE FIFTH.\n\nunicode.isdecimal()\n\n Return ``True`` if there are only decimal characters in S,\n ``False`` otherwise. Decimal characters include digit characters,\n and all characters that that can be used to form decimal-radix\n numbers, e.g. U+0660, ARABIC-INDIC DIGIT ZERO.\n', + 'string-methods': u'\nString Methods\n**************\n\nBelow are listed the string methods which both 8-bit strings and\nUnicode objects support. Note that none of these methods take keyword\narguments.\n\nIn addition, Python\'s strings support the sequence type methods\ndescribed in the *Sequence Types --- str, unicode, list, tuple,\nbuffer, xrange* section. To output formatted strings use template\nstrings or the ``%`` operator described in the *String Formatting\nOperations* section. Also, see the ``re`` module for string functions\nbased on regular expressions.\n\nstr.capitalize()\n\n Return a copy of the string with its first character capitalized\n and the rest lowercased.\n\n For 8-bit strings, this method is locale-dependent.\n\nstr.center(width[, fillchar])\n\n Return centered in a string of length *width*. Padding is done\n using the specified *fillchar* (default is a space).\n\n Changed in version 2.4: Support for the *fillchar* argument.\n\nstr.count(sub[, start[, end]])\n\n Return the number of non-overlapping occurrences of substring *sub*\n in the range [*start*, *end*]. Optional arguments *start* and\n *end* are interpreted as in slice notation.\n\nstr.decode([encoding[, errors]])\n\n Decodes the string using the codec registered for *encoding*.\n *encoding* defaults to the default string encoding. *errors* may\n be given to set a different error handling scheme. The default is\n ``\'strict\'``, meaning that encoding errors raise ``UnicodeError``.\n Other possible values are ``\'ignore\'``, ``\'replace\'`` and any other\n name registered via ``codecs.register_error()``, see section *Codec\n Base Classes*.\n\n New in version 2.2.\n\n Changed in version 2.3: Support for other error handling schemes\n added.\n\nstr.encode([encoding[, errors]])\n\n Return an encoded version of the string. Default encoding is the\n current default string encoding. *errors* may be given to set a\n different error handling scheme. The default for *errors* is\n ``\'strict\'``, meaning that encoding errors raise a\n ``UnicodeError``. Other possible values are ``\'ignore\'``,\n ``\'replace\'``, ``\'xmlcharrefreplace\'``, ``\'backslashreplace\'`` and\n any other name registered via ``codecs.register_error()``, see\n section *Codec Base Classes*. For a list of possible encodings, see\n section *Standard Encodings*.\n\n New in version 2.0.\n\n Changed in version 2.3: Support for ``\'xmlcharrefreplace\'`` and\n ``\'backslashreplace\'`` and other error handling schemes added.\n\nstr.endswith(suffix[, start[, end]])\n\n Return ``True`` if the string ends with the specified *suffix*,\n otherwise return ``False``. *suffix* can also be a tuple of\n suffixes to look for. With optional *start*, test beginning at\n that position. With optional *end*, stop comparing at that\n position.\n\n Changed in version 2.5: Accept tuples as *suffix*.\n\nstr.expandtabs([tabsize])\n\n Return a copy of the string where all tab characters are replaced\n by one or more spaces, depending on the current column and the\n given tab size. The column number is reset to zero after each\n newline occurring in the string. If *tabsize* is not given, a tab\n size of ``8`` characters is assumed. This doesn\'t understand other\n non-printing characters or escape sequences.\n\nstr.find(sub[, start[, end]])\n\n Return the lowest index in the string where substring *sub* is\n found, such that *sub* is contained in the slice ``s[start:end]``.\n Optional arguments *start* and *end* are interpreted as in slice\n notation. Return ``-1`` if *sub* is not found.\n\nstr.format(*args, **kwargs)\n\n Perform a string formatting operation. The string on which this\n method is called can contain literal text or replacement fields\n delimited by braces ``{}``. Each replacement field contains either\n the numeric index of a positional argument, or the name of a\n keyword argument. Returns a copy of the string where each\n replacement field is replaced with the string value of the\n corresponding argument.\n\n >>> "The sum of 1 + 2 is {0}".format(1+2)\n \'The sum of 1 + 2 is 3\'\n\n See *Format String Syntax* for a description of the various\n formatting options that can be specified in format strings.\n\n This method of string formatting is the new standard in Python 3.0,\n and should be preferred to the ``%`` formatting described in\n *String Formatting Operations* in new code.\n\n New in version 2.6.\n\nstr.index(sub[, start[, end]])\n\n Like ``find()``, but raise ``ValueError`` when the substring is not\n found.\n\nstr.isalnum()\n\n Return true if all characters in the string are alphanumeric and\n there is at least one character, false otherwise.\n\n For 8-bit strings, this method is locale-dependent.\n\nstr.isalpha()\n\n Return true if all characters in the string are alphabetic and\n there is at least one character, false otherwise.\n\n For 8-bit strings, this method is locale-dependent.\n\nstr.isdigit()\n\n Return true if all characters in the string are digits and there is\n at least one character, false otherwise.\n\n For 8-bit strings, this method is locale-dependent.\n\nstr.islower()\n\n Return true if all cased characters in the string are lowercase and\n there is at least one cased character, false otherwise.\n\n For 8-bit strings, this method is locale-dependent.\n\nstr.isspace()\n\n Return true if there are only whitespace characters in the string\n and there is at least one character, false otherwise.\n\n For 8-bit strings, this method is locale-dependent.\n\nstr.istitle()\n\n Return true if the string is a titlecased string and there is at\n least one character, for example uppercase characters may only\n follow uncased characters and lowercase characters only cased ones.\n Return false otherwise.\n\n For 8-bit strings, this method is locale-dependent.\n\nstr.isupper()\n\n Return true if all cased characters in the string are uppercase and\n there is at least one cased character, false otherwise.\n\n For 8-bit strings, this method is locale-dependent.\n\nstr.join(iterable)\n\n Return a string which is the concatenation of the strings in the\n *iterable* *iterable*. The separator between elements is the\n string providing this method.\n\nstr.ljust(width[, fillchar])\n\n Return the string left justified in a string of length *width*.\n Padding is done using the specified *fillchar* (default is a\n space). The original string is returned if *width* is less than\n ``len(s)``.\n\n Changed in version 2.4: Support for the *fillchar* argument.\n\nstr.lower()\n\n Return a copy of the string converted to lowercase.\n\n For 8-bit strings, this method is locale-dependent.\n\nstr.lstrip([chars])\n\n Return a copy of the string with leading characters removed. The\n *chars* argument is a string specifying the set of characters to be\n removed. If omitted or ``None``, the *chars* argument defaults to\n removing whitespace. The *chars* argument is not a prefix; rather,\n all combinations of its values are stripped:\n\n >>> \' spacious \'.lstrip()\n \'spacious \'\n >>> \'www.example.com\'.lstrip(\'cmowz.\')\n \'example.com\'\n\n Changed in version 2.2.2: Support for the *chars* argument.\n\nstr.partition(sep)\n\n Split the string at the first occurrence of *sep*, and return a\n 3-tuple containing the part before the separator, the separator\n itself, and the part after the separator. If the separator is not\n found, return a 3-tuple containing the string itself, followed by\n two empty strings.\n\n New in version 2.5.\n\nstr.replace(old, new[, count])\n\n Return a copy of the string with all occurrences of substring *old*\n replaced by *new*. If the optional argument *count* is given, only\n the first *count* occurrences are replaced.\n\nstr.rfind(sub[, start[, end]])\n\n Return the highest index in the string where substring *sub* is\n found, such that *sub* is contained within ``s[start:end]``.\n Optional arguments *start* and *end* are interpreted as in slice\n notation. Return ``-1`` on failure.\n\nstr.rindex(sub[, start[, end]])\n\n Like ``rfind()`` but raises ``ValueError`` when the substring *sub*\n is not found.\n\nstr.rjust(width[, fillchar])\n\n Return the string right justified in a string of length *width*.\n Padding is done using the specified *fillchar* (default is a\n space). The original string is returned if *width* is less than\n ``len(s)``.\n\n Changed in version 2.4: Support for the *fillchar* argument.\n\nstr.rpartition(sep)\n\n Split the string at the last occurrence of *sep*, and return a\n 3-tuple containing the part before the separator, the separator\n itself, and the part after the separator. If the separator is not\n found, return a 3-tuple containing two empty strings, followed by\n the string itself.\n\n New in version 2.5.\n\nstr.rsplit([sep[, maxsplit]])\n\n Return a list of the words in the string, using *sep* as the\n delimiter string. If *maxsplit* is given, at most *maxsplit* splits\n are done, the *rightmost* ones. If *sep* is not specified or\n ``None``, any whitespace string is a separator. Except for\n splitting from the right, ``rsplit()`` behaves like ``split()``\n which is described in detail below.\n\n New in version 2.4.\n\nstr.rstrip([chars])\n\n Return a copy of the string with trailing characters removed. The\n *chars* argument is a string specifying the set of characters to be\n removed. If omitted or ``None``, the *chars* argument defaults to\n removing whitespace. The *chars* argument is not a suffix; rather,\n all combinations of its values are stripped:\n\n >>> \' spacious \'.rstrip()\n \' spacious\'\n >>> \'mississippi\'.rstrip(\'ipz\')\n \'mississ\'\n\n Changed in version 2.2.2: Support for the *chars* argument.\n\nstr.split([sep[, maxsplit]])\n\n Return a list of the words in the string, using *sep* as the\n delimiter string. If *maxsplit* is given, at most *maxsplit*\n splits are done (thus, the list will have at most ``maxsplit+1``\n elements). If *maxsplit* is not specified, then there is no limit\n on the number of splits (all possible splits are made).\n\n If *sep* is given, consecutive delimiters are not grouped together\n and are deemed to delimit empty strings (for example,\n ``\'1,,2\'.split(\',\')`` returns ``[\'1\', \'\', \'2\']``). The *sep*\n argument may consist of multiple characters (for example,\n ``\'1<>2<>3\'.split(\'<>\')`` returns ``[\'1\', \'2\', \'3\']``). Splitting\n an empty string with a specified separator returns ``[\'\']``.\n\n If *sep* is not specified or is ``None``, a different splitting\n algorithm is applied: runs of consecutive whitespace are regarded\n as a single separator, and the result will contain no empty strings\n at the start or end if the string has leading or trailing\n whitespace. Consequently, splitting an empty string or a string\n consisting of just whitespace with a ``None`` separator returns\n ``[]``.\n\n For example, ``\' 1 2 3 \'.split()`` returns ``[\'1\', \'2\', \'3\']``,\n and ``\' 1 2 3 \'.split(None, 1)`` returns ``[\'1\', \'2 3 \']``.\n\nstr.splitlines([keepends])\n\n Return a list of the lines in the string, breaking at line\n boundaries. Line breaks are not included in the resulting list\n unless *keepends* is given and true.\n\nstr.startswith(prefix[, start[, end]])\n\n Return ``True`` if string starts with the *prefix*, otherwise\n return ``False``. *prefix* can also be a tuple of prefixes to look\n for. With optional *start*, test string beginning at that\n position. With optional *end*, stop comparing string at that\n position.\n\n Changed in version 2.5: Accept tuples as *prefix*.\n\nstr.strip([chars])\n\n Return a copy of the string with the leading and trailing\n characters removed. The *chars* argument is a string specifying the\n set of characters to be removed. If omitted or ``None``, the\n *chars* argument defaults to removing whitespace. The *chars*\n argument is not a prefix or suffix; rather, all combinations of its\n values are stripped:\n\n >>> \' spacious \'.strip()\n \'spacious\'\n >>> \'www.example.com\'.strip(\'cmowz.\')\n \'example\'\n\n Changed in version 2.2.2: Support for the *chars* argument.\n\nstr.swapcase()\n\n Return a copy of the string with uppercase characters converted to\n lowercase and vice versa.\n\n For 8-bit strings, this method is locale-dependent.\n\nstr.title()\n\n Return a titlecased version of the string where words start with an\n uppercase character and the remaining characters are lowercase.\n\n The algorithm uses a simple language-independent definition of a\n word as groups of consecutive letters. The definition works in\n many contexts but it means that apostrophes in contractions and\n possessives form word boundaries, which may not be the desired\n result:\n\n >>> "they\'re bill\'s friends from the UK".title()\n "They\'Re Bill\'S Friends From The Uk"\n\n A workaround for apostrophes can be constructed using regular\n expressions:\n\n >>> import re\n >>> def titlecase(s):\n return re.sub(r"[A-Za-z]+(\'[A-Za-z]+)?",\n lambda mo: mo.group(0)[0].upper() +\n mo.group(0)[1:].lower(),\n s)\n\n >>> titlecase("they\'re bill\'s friends.")\n "They\'re Bill\'s Friends."\n\n For 8-bit strings, this method is locale-dependent.\n\nstr.translate(table[, deletechars])\n\n Return a copy of the string where all characters occurring in the\n optional argument *deletechars* are removed, and the remaining\n characters have been mapped through the given translation table,\n which must be a string of length 256.\n\n You can use the ``maketrans()`` helper function in the ``string``\n module to create a translation table. For string objects, set the\n *table* argument to ``None`` for translations that only delete\n characters:\n\n >>> \'read this short text\'.translate(None, \'aeiou\')\n \'rd ths shrt txt\'\n\n New in version 2.6: Support for a ``None`` *table* argument.\n\n For Unicode objects, the ``translate()`` method does not accept the\n optional *deletechars* argument. Instead, it returns a copy of the\n *s* where all characters have been mapped through the given\n translation table which must be a mapping of Unicode ordinals to\n Unicode ordinals, Unicode strings or ``None``. Unmapped characters\n are left untouched. Characters mapped to ``None`` are deleted.\n Note, a more flexible approach is to create a custom character\n mapping codec using the ``codecs`` module (see ``encodings.cp1251``\n for an example).\n\nstr.upper()\n\n Return a copy of the string converted to uppercase.\n\n For 8-bit strings, this method is locale-dependent.\n\nstr.zfill(width)\n\n Return the numeric string left filled with zeros in a string of\n length *width*. A sign prefix is handled correctly. The original\n string is returned if *width* is less than ``len(s)``.\n\n New in version 2.2.2.\n\nThe following methods are present only on unicode objects:\n\nunicode.isnumeric()\n\n Return ``True`` if there are only numeric characters in S,\n ``False`` otherwise. Numeric characters include digit characters,\n and all characters that have the Unicode numeric value property,\n e.g. U+2155, VULGAR FRACTION ONE FIFTH.\n\nunicode.isdecimal()\n\n Return ``True`` if there are only decimal characters in S,\n ``False`` otherwise. Decimal characters include digit characters,\n and all characters that that can be used to form decimal-radix\n numbers, e.g. U+0660, ARABIC-INDIC DIGIT ZERO.\n', 'strings': u'\nString literals\n***************\n\nString literals are described by the following lexical definitions:\n\n stringliteral ::= [stringprefix](shortstring | longstring)\n stringprefix ::= "r" | "u" | "ur" | "R" | "U" | "UR" | "Ur" | "uR"\n shortstring ::= "\'" shortstringitem* "\'" | \'"\' shortstringitem* \'"\'\n longstring ::= "\'\'\'" longstringitem* "\'\'\'"\n | \'"""\' longstringitem* \'"""\'\n shortstringitem ::= shortstringchar | escapeseq\n longstringitem ::= longstringchar | escapeseq\n shortstringchar ::= \n longstringchar ::= \n escapeseq ::= "\\" \n\nOne syntactic restriction not indicated by these productions is that\nwhitespace is not allowed between the **stringprefix** and the rest of\nthe string literal. The source character set is defined by the\nencoding declaration; it is ASCII if no encoding declaration is given\nin the source file; see section *Encoding declarations*.\n\nIn plain English: String literals can be enclosed in matching single\nquotes (``\'``) or double quotes (``"``). They can also be enclosed in\nmatching groups of three single or double quotes (these are generally\nreferred to as *triple-quoted strings*). The backslash (``\\``)\ncharacter is used to escape characters that otherwise have a special\nmeaning, such as newline, backslash itself, or the quote character.\nString literals may optionally be prefixed with a letter ``\'r\'`` or\n``\'R\'``; such strings are called *raw strings* and use different rules\nfor interpreting backslash escape sequences. A prefix of ``\'u\'`` or\n``\'U\'`` makes the string a Unicode string. Unicode strings use the\nUnicode character set as defined by the Unicode Consortium and ISO\n10646. Some additional escape sequences, described below, are\navailable in Unicode strings. The two prefix characters may be\ncombined; in this case, ``\'u\'`` must appear before ``\'r\'``.\n\nIn triple-quoted strings, unescaped newlines and quotes are allowed\n(and are retained), except that three unescaped quotes in a row\nterminate the string. (A "quote" is the character used to open the\nstring, i.e. either ``\'`` or ``"``.)\n\nUnless an ``\'r\'`` or ``\'R\'`` prefix is present, escape sequences in\nstrings are interpreted according to rules similar to those used by\nStandard C. The recognized escape sequences are:\n\n+-------------------+-----------------------------------+---------+\n| Escape Sequence | Meaning | Notes |\n+===================+===================================+=========+\n| ``\\newline`` | Ignored | |\n+-------------------+-----------------------------------+---------+\n| ``\\\\`` | Backslash (``\\``) | |\n+-------------------+-----------------------------------+---------+\n| ``\\\'`` | Single quote (``\'``) | |\n+-------------------+-----------------------------------+---------+\n| ``\\"`` | Double quote (``"``) | |\n+-------------------+-----------------------------------+---------+\n| ``\\a`` | ASCII Bell (BEL) | |\n+-------------------+-----------------------------------+---------+\n| ``\\b`` | ASCII Backspace (BS) | |\n+-------------------+-----------------------------------+---------+\n| ``\\f`` | ASCII Formfeed (FF) | |\n+-------------------+-----------------------------------+---------+\n| ``\\n`` | ASCII Linefeed (LF) | |\n+-------------------+-----------------------------------+---------+\n| ``\\N{name}`` | Character named *name* in the | |\n| | Unicode database (Unicode only) | |\n+-------------------+-----------------------------------+---------+\n| ``\\r`` | ASCII Carriage Return (CR) | |\n+-------------------+-----------------------------------+---------+\n| ``\\t`` | ASCII Horizontal Tab (TAB) | |\n+-------------------+-----------------------------------+---------+\n| ``\\uxxxx`` | Character with 16-bit hex value | (1) |\n| | *xxxx* (Unicode only) | |\n+-------------------+-----------------------------------+---------+\n| ``\\Uxxxxxxxx`` | Character with 32-bit hex value | (2) |\n| | *xxxxxxxx* (Unicode only) | |\n+-------------------+-----------------------------------+---------+\n| ``\\v`` | ASCII Vertical Tab (VT) | |\n+-------------------+-----------------------------------+---------+\n| ``\\ooo`` | Character with octal value *ooo* | (3,5) |\n+-------------------+-----------------------------------+---------+\n| ``\\xhh`` | Character with hex value *hh* | (4,5) |\n+-------------------+-----------------------------------+---------+\n\nNotes:\n\n1. Individual code units which form parts of a surrogate pair can be\n encoded using this escape sequence.\n\n2. Any Unicode character can be encoded this way, but characters\n outside the Basic Multilingual Plane (BMP) will be encoded using a\n surrogate pair if Python is compiled to use 16-bit code units (the\n default). Individual code units which form parts of a surrogate\n pair can be encoded using this escape sequence.\n\n3. As in Standard C, up to three octal digits are accepted.\n\n4. Unlike in Standard C, exactly two hex digits are required.\n\n5. In a string literal, hexadecimal and octal escapes denote the byte\n with the given value; it is not necessary that the byte encodes a\n character in the source character set. In a Unicode literal, these\n escapes denote a Unicode character with the given value.\n\nUnlike Standard C, all unrecognized escape sequences are left in the\nstring unchanged, i.e., *the backslash is left in the string*. (This\nbehavior is useful when debugging: if an escape sequence is mistyped,\nthe resulting output is more easily recognized as broken.) It is also\nimportant to note that the escape sequences marked as "(Unicode only)"\nin the table above fall into the category of unrecognized escapes for\nnon-Unicode string literals.\n\nWhen an ``\'r\'`` or ``\'R\'`` prefix is present, a character following a\nbackslash is included in the string without change, and *all\nbackslashes are left in the string*. For example, the string literal\n``r"\\n"`` consists of two characters: a backslash and a lowercase\n``\'n\'``. String quotes can be escaped with a backslash, but the\nbackslash remains in the string; for example, ``r"\\""`` is a valid\nstring literal consisting of two characters: a backslash and a double\nquote; ``r"\\"`` is not a valid string literal (even a raw string\ncannot end in an odd number of backslashes). Specifically, *a raw\nstring cannot end in a single backslash* (since the backslash would\nescape the following quote character). Note also that a single\nbackslash followed by a newline is interpreted as those two characters\nas part of the string, *not* as a line continuation.\n\nWhen an ``\'r\'`` or ``\'R\'`` prefix is used in conjunction with a\n``\'u\'`` or ``\'U\'`` prefix, then the ``\\uXXXX`` and ``\\UXXXXXXXX``\nescape sequences are processed while *all other backslashes are left\nin the string*. For example, the string literal ``ur"\\u0062\\n"``\nconsists of three Unicode characters: \'LATIN SMALL LETTER B\', \'REVERSE\nSOLIDUS\', and \'LATIN SMALL LETTER N\'. Backslashes can be escaped with\na preceding backslash; however, both remain in the string. As a\nresult, ``\\uXXXX`` escape sequences are only recognized when there are\nan odd number of backslashes.\n', 'subscriptions': u'\nSubscriptions\n*************\n\nA subscription selects an item of a sequence (string, tuple or list)\nor mapping (dictionary) object:\n\n subscription ::= primary "[" expression_list "]"\n\nThe primary must evaluate to an object of a sequence or mapping type.\n\nIf the primary is a mapping, the expression list must evaluate to an\nobject whose value is one of the keys of the mapping, and the\nsubscription selects the value in the mapping that corresponds to that\nkey. (The expression list is a tuple except if it has exactly one\nitem.)\n\nIf the primary is a sequence, the expression (list) must evaluate to a\nplain integer. If this value is negative, the length of the sequence\nis added to it (so that, e.g., ``x[-1]`` selects the last item of\n``x``.) The resulting value must be a nonnegative integer less than\nthe number of items in the sequence, and the subscription selects the\nitem whose index is that value (counting from zero).\n\nA string\'s items are characters. A character is not a separate data\ntype but a string of exactly one character.\n', 'truth': u"\nTruth Value Testing\n*******************\n\nAny object can be tested for truth value, for use in an ``if`` or\n``while`` condition or as operand of the Boolean operations below. The\nfollowing values are considered false:\n\n* ``None``\n\n* ``False``\n\n* zero of any numeric type, for example, ``0``, ``0L``, ``0.0``,\n ``0j``.\n\n* any empty sequence, for example, ``''``, ``()``, ``[]``.\n\n* any empty mapping, for example, ``{}``.\n\n* instances of user-defined classes, if the class defines a\n ``__nonzero__()`` or ``__len__()`` method, when that method returns\n the integer zero or ``bool`` value ``False``. [1]\n\nAll other values are considered true --- so objects of many types are\nalways true.\n\nOperations and built-in functions that have a Boolean result always\nreturn ``0`` or ``False`` for false and ``1`` or ``True`` for true,\nunless otherwise stated. (Important exception: the Boolean operations\n``or`` and ``and`` always return one of their operands.)\n", @@ -75,7 +75,7 @@ 'typesmapping': u'\nMapping Types --- ``dict``\n**************************\n\nA *mapping* object maps *hashable* values to arbitrary objects.\nMappings are mutable objects. There is currently only one standard\nmapping type, the *dictionary*. (For other containers see the built\nin ``list``, ``set``, and ``tuple`` classes, and the ``collections``\nmodule.)\n\nA dictionary\'s keys are *almost* arbitrary values. Values that are\nnot *hashable*, that is, values containing lists, dictionaries or\nother mutable types (that are compared by value rather than by object\nidentity) may not be used as keys. Numeric types used for keys obey\nthe normal rules for numeric comparison: if two numbers compare equal\n(such as ``1`` and ``1.0``) then they can be used interchangeably to\nindex the same dictionary entry. (Note however, that since computers\nstore floating-point numbers as approximations it is usually unwise to\nuse them as dictionary keys.)\n\nDictionaries can be created by placing a comma-separated list of\n``key: value`` pairs within braces, for example: ``{\'jack\': 4098,\n\'sjoerd\': 4127}`` or ``{4098: \'jack\', 4127: \'sjoerd\'}``, or by the\n``dict`` constructor.\n\nclass class dict([arg])\n\n Return a new dictionary initialized from an optional positional\n argument or from a set of keyword arguments. If no arguments are\n given, return a new empty dictionary. If the positional argument\n *arg* is a mapping object, return a dictionary mapping the same\n keys to the same values as does the mapping object. Otherwise the\n positional argument must be a sequence, a container that supports\n iteration, or an iterator object. The elements of the argument\n must each also be of one of those kinds, and each must in turn\n contain exactly two objects. The first is used as a key in the new\n dictionary, and the second as the key\'s value. If a given key is\n seen more than once, the last value associated with it is retained\n in the new dictionary.\n\n If keyword arguments are given, the keywords themselves with their\n associated values are added as items to the dictionary. If a key is\n specified both in the positional argument and as a keyword\n argument, the value associated with the keyword is retained in the\n dictionary. For example, these all return a dictionary equal to\n ``{"one": 2, "two": 3}``:\n\n * ``dict(one=2, two=3)``\n\n * ``dict({\'one\': 2, \'two\': 3})``\n\n * ``dict(zip((\'one\', \'two\'), (2, 3)))``\n\n * ``dict([[\'two\', 3], [\'one\', 2]])``\n\n The first example only works for keys that are valid Python\n identifiers; the others work with any valid keys.\n\n New in version 2.2.\n\n Changed in version 2.3: Support for building a dictionary from\n keyword arguments added.\n\n These are the operations that dictionaries support (and therefore,\n custom mapping types should support too):\n\n len(d)\n\n Return the number of items in the dictionary *d*.\n\n d[key]\n\n Return the item of *d* with key *key*. Raises a ``KeyError`` if\n *key* is not in the map.\n\n New in version 2.5: If a subclass of dict defines a method\n ``__missing__()``, if the key *key* is not present, the\n ``d[key]`` operation calls that method with the key *key* as\n argument. The ``d[key]`` operation then returns or raises\n whatever is returned or raised by the ``__missing__(key)`` call\n if the key is not present. No other operations or methods invoke\n ``__missing__()``. If ``__missing__()`` is not defined,\n ``KeyError`` is raised. ``__missing__()`` must be a method; it\n cannot be an instance variable. For an example, see\n ``collections.defaultdict``.\n\n d[key] = value\n\n Set ``d[key]`` to *value*.\n\n del d[key]\n\n Remove ``d[key]`` from *d*. Raises a ``KeyError`` if *key* is\n not in the map.\n\n key in d\n\n Return ``True`` if *d* has a key *key*, else ``False``.\n\n New in version 2.2.\n\n key not in d\n\n Equivalent to ``not key in d``.\n\n New in version 2.2.\n\n iter(d)\n\n Return an iterator over the keys of the dictionary. This is a\n shortcut for ``iterkeys()``.\n\n clear()\n\n Remove all items from the dictionary.\n\n copy()\n\n Return a shallow copy of the dictionary.\n\n fromkeys(seq[, value])\n\n Create a new dictionary with keys from *seq* and values set to\n *value*.\n\n ``fromkeys()`` is a class method that returns a new dictionary.\n *value* defaults to ``None``.\n\n New in version 2.3.\n\n get(key[, default])\n\n Return the value for *key* if *key* is in the dictionary, else\n *default*. If *default* is not given, it defaults to ``None``,\n so that this method never raises a ``KeyError``.\n\n has_key(key)\n\n Test for the presence of *key* in the dictionary. ``has_key()``\n is deprecated in favor of ``key in d``.\n\n items()\n\n Return a copy of the dictionary\'s list of ``(key, value)``\n pairs.\n\n **CPython implementation detail:** Keys and values are listed in\n an arbitrary order which is non-random, varies across Python\n implementations, and depends on the dictionary\'s history of\n insertions and deletions.\n\n If ``items()``, ``keys()``, ``values()``, ``iteritems()``,\n ``iterkeys()``, and ``itervalues()`` are called with no\n intervening modifications to the dictionary, the lists will\n directly correspond. This allows the creation of ``(value,\n key)`` pairs using ``zip()``: ``pairs = zip(d.values(),\n d.keys())``. The same relationship holds for the ``iterkeys()``\n and ``itervalues()`` methods: ``pairs = zip(d.itervalues(),\n d.iterkeys())`` provides the same value for ``pairs``. Another\n way to create the same list is ``pairs = [(v, k) for (k, v) in\n d.iteritems()]``.\n\n iteritems()\n\n Return an iterator over the dictionary\'s ``(key, value)`` pairs.\n See the note for ``dict.items()``.\n\n Using ``iteritems()`` while adding or deleting entries in the\n dictionary may raise a ``RuntimeError`` or fail to iterate over\n all entries.\n\n New in version 2.2.\n\n iterkeys()\n\n Return an iterator over the dictionary\'s keys. See the note for\n ``dict.items()``.\n\n Using ``iterkeys()`` while adding or deleting entries in the\n dictionary may raise a ``RuntimeError`` or fail to iterate over\n all entries.\n\n New in version 2.2.\n\n itervalues()\n\n Return an iterator over the dictionary\'s values. See the note\n for ``dict.items()``.\n\n Using ``itervalues()`` while adding or deleting entries in the\n dictionary may raise a ``RuntimeError`` or fail to iterate over\n all entries.\n\n New in version 2.2.\n\n keys()\n\n Return a copy of the dictionary\'s list of keys. See the note\n for ``dict.items()``.\n\n pop(key[, default])\n\n If *key* is in the dictionary, remove it and return its value,\n else return *default*. If *default* is not given and *key* is\n not in the dictionary, a ``KeyError`` is raised.\n\n New in version 2.3.\n\n popitem()\n\n Remove and return an arbitrary ``(key, value)`` pair from the\n dictionary.\n\n ``popitem()`` is useful to destructively iterate over a\n dictionary, as often used in set algorithms. If the dictionary\n is empty, calling ``popitem()`` raises a ``KeyError``.\n\n setdefault(key[, default])\n\n If *key* is in the dictionary, return its value. If not, insert\n *key* with a value of *default* and return *default*. *default*\n defaults to ``None``.\n\n update([other])\n\n Update the dictionary with the key/value pairs from *other*,\n overwriting existing keys. Return ``None``.\n\n ``update()`` accepts either another dictionary object or an\n iterable of key/value pairs (as a tuple or other iterable of\n length two). If keyword arguments are specified, the dictionary\n is then updated with those key/value pairs: ``d.update(red=1,\n blue=2)``.\n\n Changed in version 2.4: Allowed the argument to be an iterable\n of key/value pairs and allowed keyword arguments.\n\n values()\n\n Return a copy of the dictionary\'s list of values. See the note\n for ``dict.items()``.\n', 'typesmethods': u"\nMethods\n*******\n\nMethods are functions that are called using the attribute notation.\nThere are two flavors: built-in methods (such as ``append()`` on\nlists) and class instance methods. Built-in methods are described\nwith the types that support them.\n\nThe implementation adds two special read-only attributes to class\ninstance methods: ``m.im_self`` is the object on which the method\noperates, and ``m.im_func`` is the function implementing the method.\nCalling ``m(arg-1, arg-2, ..., arg-n)`` is completely equivalent to\ncalling ``m.im_func(m.im_self, arg-1, arg-2, ..., arg-n)``.\n\nClass instance methods are either *bound* or *unbound*, referring to\nwhether the method was accessed through an instance or a class,\nrespectively. When a method is unbound, its ``im_self`` attribute\nwill be ``None`` and if called, an explicit ``self`` object must be\npassed as the first argument. In this case, ``self`` must be an\ninstance of the unbound method's class (or a subclass of that class),\notherwise a ``TypeError`` is raised.\n\nLike function objects, methods objects support getting arbitrary\nattributes. However, since method attributes are actually stored on\nthe underlying function object (``meth.im_func``), setting method\nattributes on either bound or unbound methods is disallowed.\nAttempting to set a method attribute results in a ``TypeError`` being\nraised. In order to set a method attribute, you need to explicitly\nset it on the underlying function object:\n\n class C:\n def method(self):\n pass\n\n c = C()\n c.method.im_func.whoami = 'my name is c'\n\nSee *The standard type hierarchy* for more information.\n", 'typesmodules': u"\nModules\n*******\n\nThe only special operation on a module is attribute access:\n``m.name``, where *m* is a module and *name* accesses a name defined\nin *m*'s symbol table. Module attributes can be assigned to. (Note\nthat the ``import`` statement is not, strictly speaking, an operation\non a module object; ``import foo`` does not require a module object\nnamed *foo* to exist, rather it requires an (external) *definition*\nfor a module named *foo* somewhere.)\n\nA special member of every module is ``__dict__``. This is the\ndictionary containing the module's symbol table. Modifying this\ndictionary will actually change the module's symbol table, but direct\nassignment to the ``__dict__`` attribute is not possible (you can\nwrite ``m.__dict__['a'] = 1``, which defines ``m.a`` to be ``1``, but\nyou can't write ``m.__dict__ = {}``). Modifying ``__dict__`` directly\nis not recommended.\n\nModules built into the interpreter are written like this: ````. If loaded from a file, they are written as\n````.\n", - 'typesseq': u'\nSequence Types --- ``str``, ``unicode``, ``list``, ``tuple``, ``buffer``, ``xrange``\n************************************************************************************\n\nThere are six sequence types: strings, Unicode strings, lists, tuples,\nbuffers, and xrange objects.\n\nFor other containers see the built in ``dict`` and ``set`` classes,\nand the ``collections`` module.\n\nString literals are written in single or double quotes: ``\'xyzzy\'``,\n``"frobozz"``. See *String literals* for more about string literals.\nUnicode strings are much like strings, but are specified in the syntax\nusing a preceding ``\'u\'`` character: ``u\'abc\'``, ``u"def"``. In\naddition to the functionality described here, there are also string-\nspecific methods described in the *String Methods* section. Lists are\nconstructed with square brackets, separating items with commas: ``[a,\nb, c]``. Tuples are constructed by the comma operator (not within\nsquare brackets), with or without enclosing parentheses, but an empty\ntuple must have the enclosing parentheses, such as ``a, b, c`` or\n``()``. A single item tuple must have a trailing comma, such as\n``(d,)``.\n\nBuffer objects are not directly supported by Python syntax, but can be\ncreated by calling the built-in function ``buffer()``. They don\'t\nsupport concatenation or repetition.\n\nObjects of type xrange are similar to buffers in that there is no\nspecific syntax to create them, but they are created using the\n``xrange()`` function. They don\'t support slicing, concatenation or\nrepetition, and using ``in``, ``not in``, ``min()`` or ``max()`` on\nthem is inefficient.\n\nMost sequence types support the following operations. The ``in`` and\n``not in`` operations have the same priorities as the comparison\noperations. The ``+`` and ``*`` operations have the same priority as\nthe corresponding numeric operations. [3] Additional methods are\nprovided for *Mutable Sequence Types*.\n\nThis table lists the sequence operations sorted in ascending priority\n(operations in the same box have the same priority). In the table,\n*s* and *t* are sequences of the same type; *n*, *i* and *j* are\nintegers:\n\n+--------------------+----------------------------------+------------+\n| Operation | Result | Notes |\n+====================+==================================+============+\n| ``x in s`` | ``True`` if an item of *s* is | (1) |\n| | equal to *x*, else ``False`` | |\n+--------------------+----------------------------------+------------+\n| ``x not in s`` | ``False`` if an item of *s* is | (1) |\n| | equal to *x*, else ``True`` | |\n+--------------------+----------------------------------+------------+\n| ``s + t`` | the concatenation of *s* and *t* | (6) |\n+--------------------+----------------------------------+------------+\n| ``s * n, n * s`` | *n* shallow copies of *s* | (2) |\n| | concatenated | |\n+--------------------+----------------------------------+------------+\n| ``s[i]`` | *i*\'th item of *s*, origin 0 | (3) |\n+--------------------+----------------------------------+------------+\n| ``s[i:j]`` | slice of *s* from *i* to *j* | (3)(4) |\n+--------------------+----------------------------------+------------+\n| ``s[i:j:k]`` | slice of *s* from *i* to *j* | (3)(5) |\n| | with step *k* | |\n+--------------------+----------------------------------+------------+\n| ``len(s)`` | length of *s* | |\n+--------------------+----------------------------------+------------+\n| ``min(s)`` | smallest item of *s* | |\n+--------------------+----------------------------------+------------+\n| ``max(s)`` | largest item of *s* | |\n+--------------------+----------------------------------+------------+\n\nSequence types also support comparisons. In particular, tuples and\nlists are compared lexicographically by comparing corresponding\nelements. This means that to compare equal, every element must compare\nequal and the two sequences must be of the same type and have the same\nlength. (For full details see *Comparisons* in the language\nreference.)\n\nNotes:\n\n1. When *s* is a string or Unicode string object the ``in`` and ``not\n in`` operations act like a substring test. In Python versions\n before 2.3, *x* had to be a string of length 1. In Python 2.3 and\n beyond, *x* may be a string of any length.\n\n2. Values of *n* less than ``0`` are treated as ``0`` (which yields an\n empty sequence of the same type as *s*). Note also that the copies\n are shallow; nested structures are not copied. This often haunts\n new Python programmers; consider:\n\n >>> lists = [[]] * 3\n >>> lists\n [[], [], []]\n >>> lists[0].append(3)\n >>> lists\n [[3], [3], [3]]\n\n What has happened is that ``[[]]`` is a one-element list containing\n an empty list, so all three elements of ``[[]] * 3`` are (pointers\n to) this single empty list. Modifying any of the elements of\n ``lists`` modifies this single list. You can create a list of\n different lists this way:\n\n >>> lists = [[] for i in range(3)]\n >>> lists[0].append(3)\n >>> lists[1].append(5)\n >>> lists[2].append(7)\n >>> lists\n [[3], [5], [7]]\n\n3. If *i* or *j* is negative, the index is relative to the end of the\n string: ``len(s) + i`` or ``len(s) + j`` is substituted. But note\n that ``-0`` is still ``0``.\n\n4. The slice of *s* from *i* to *j* is defined as the sequence of\n items with index *k* such that ``i <= k < j``. If *i* or *j* is\n greater than ``len(s)``, use ``len(s)``. If *i* is omitted or\n ``None``, use ``0``. If *j* is omitted or ``None``, use\n ``len(s)``. If *i* is greater than or equal to *j*, the slice is\n empty.\n\n5. The slice of *s* from *i* to *j* with step *k* is defined as the\n sequence of items with index ``x = i + n*k`` such that ``0 <= n <\n (j-i)/k``. In other words, the indices are ``i``, ``i+k``,\n ``i+2*k``, ``i+3*k`` and so on, stopping when *j* is reached (but\n never including *j*). If *i* or *j* is greater than ``len(s)``,\n use ``len(s)``. If *i* or *j* are omitted or ``None``, they become\n "end" values (which end depends on the sign of *k*). Note, *k*\n cannot be zero. If *k* is ``None``, it is treated like ``1``.\n\n6. **CPython implementation detail:** If *s* and *t* are both strings,\n some Python implementations such as CPython can usually perform an\n in-place optimization for assignments of the form ``s = s + t`` or\n ``s += t``. When applicable, this optimization makes quadratic\n run-time much less likely. This optimization is both version and\n implementation dependent. For performance sensitive code, it is\n preferable to use the ``str.join()`` method which assures\n consistent linear concatenation performance across versions and\n implementations.\n\n Changed in version 2.4: Formerly, string concatenation never\n occurred in-place.\n\n\nString Methods\n==============\n\nBelow are listed the string methods which both 8-bit strings and\nUnicode objects support. Note that none of these methods take keyword\narguments.\n\nIn addition, Python\'s strings support the sequence type methods\ndescribed in the *Sequence Types --- str, unicode, list, tuple,\nbuffer, xrange* section. To output formatted strings use template\nstrings or the ``%`` operator described in the *String Formatting\nOperations* section. Also, see the ``re`` module for string functions\nbased on regular expressions.\n\nstr.capitalize()\n\n Return a copy of the string with only its first character\n capitalized.\n\n For 8-bit strings, this method is locale-dependent.\n\nstr.center(width[, fillchar])\n\n Return centered in a string of length *width*. Padding is done\n using the specified *fillchar* (default is a space).\n\n Changed in version 2.4: Support for the *fillchar* argument.\n\nstr.count(sub[, start[, end]])\n\n Return the number of non-overlapping occurrences of substring *sub*\n in the range [*start*, *end*]. Optional arguments *start* and\n *end* are interpreted as in slice notation.\n\nstr.decode([encoding[, errors]])\n\n Decodes the string using the codec registered for *encoding*.\n *encoding* defaults to the default string encoding. *errors* may\n be given to set a different error handling scheme. The default is\n ``\'strict\'``, meaning that encoding errors raise ``UnicodeError``.\n Other possible values are ``\'ignore\'``, ``\'replace\'`` and any other\n name registered via ``codecs.register_error()``, see section *Codec\n Base Classes*.\n\n New in version 2.2.\n\n Changed in version 2.3: Support for other error handling schemes\n added.\n\nstr.encode([encoding[, errors]])\n\n Return an encoded version of the string. Default encoding is the\n current default string encoding. *errors* may be given to set a\n different error handling scheme. The default for *errors* is\n ``\'strict\'``, meaning that encoding errors raise a\n ``UnicodeError``. Other possible values are ``\'ignore\'``,\n ``\'replace\'``, ``\'xmlcharrefreplace\'``, ``\'backslashreplace\'`` and\n any other name registered via ``codecs.register_error()``, see\n section *Codec Base Classes*. For a list of possible encodings, see\n section *Standard Encodings*.\n\n New in version 2.0.\n\n Changed in version 2.3: Support for ``\'xmlcharrefreplace\'`` and\n ``\'backslashreplace\'`` and other error handling schemes added.\n\nstr.endswith(suffix[, start[, end]])\n\n Return ``True`` if the string ends with the specified *suffix*,\n otherwise return ``False``. *suffix* can also be a tuple of\n suffixes to look for. With optional *start*, test beginning at\n that position. With optional *end*, stop comparing at that\n position.\n\n Changed in version 2.5: Accept tuples as *suffix*.\n\nstr.expandtabs([tabsize])\n\n Return a copy of the string where all tab characters are replaced\n by one or more spaces, depending on the current column and the\n given tab size. The column number is reset to zero after each\n newline occurring in the string. If *tabsize* is not given, a tab\n size of ``8`` characters is assumed. This doesn\'t understand other\n non-printing characters or escape sequences.\n\nstr.find(sub[, start[, end]])\n\n Return the lowest index in the string where substring *sub* is\n found, such that *sub* is contained in the range [*start*, *end*].\n Optional arguments *start* and *end* are interpreted as in slice\n notation. Return ``-1`` if *sub* is not found.\n\nstr.format(*args, **kwargs)\n\n Perform a string formatting operation. The *format_string*\n argument can contain literal text or replacement fields delimited\n by braces ``{}``. Each replacement field contains either the\n numeric index of a positional argument, or the name of a keyword\n argument. Returns a copy of *format_string* where each replacement\n field is replaced with the string value of the corresponding\n argument.\n\n >>> "The sum of 1 + 2 is {0}".format(1+2)\n \'The sum of 1 + 2 is 3\'\n\n See *Format String Syntax* for a description of the various\n formatting options that can be specified in format strings.\n\n This method of string formatting is the new standard in Python 3.0,\n and should be preferred to the ``%`` formatting described in\n *String Formatting Operations* in new code.\n\n New in version 2.6.\n\nstr.index(sub[, start[, end]])\n\n Like ``find()``, but raise ``ValueError`` when the substring is not\n found.\n\nstr.isalnum()\n\n Return true if all characters in the string are alphanumeric and\n there is at least one character, false otherwise.\n\n For 8-bit strings, this method is locale-dependent.\n\nstr.isalpha()\n\n Return true if all characters in the string are alphabetic and\n there is at least one character, false otherwise.\n\n For 8-bit strings, this method is locale-dependent.\n\nstr.isdigit()\n\n Return true if all characters in the string are digits and there is\n at least one character, false otherwise.\n\n For 8-bit strings, this method is locale-dependent.\n\nstr.islower()\n\n Return true if all cased characters in the string are lowercase and\n there is at least one cased character, false otherwise.\n\n For 8-bit strings, this method is locale-dependent.\n\nstr.isspace()\n\n Return true if there are only whitespace characters in the string\n and there is at least one character, false otherwise.\n\n For 8-bit strings, this method is locale-dependent.\n\nstr.istitle()\n\n Return true if the string is a titlecased string and there is at\n least one character, for example uppercase characters may only\n follow uncased characters and lowercase characters only cased ones.\n Return false otherwise.\n\n For 8-bit strings, this method is locale-dependent.\n\nstr.isupper()\n\n Return true if all cased characters in the string are uppercase and\n there is at least one cased character, false otherwise.\n\n For 8-bit strings, this method is locale-dependent.\n\nstr.join(iterable)\n\n Return a string which is the concatenation of the strings in the\n *iterable* *iterable*. The separator between elements is the\n string providing this method.\n\nstr.ljust(width[, fillchar])\n\n Return the string left justified in a string of length *width*.\n Padding is done using the specified *fillchar* (default is a\n space). The original string is returned if *width* is less than\n ``len(s)``.\n\n Changed in version 2.4: Support for the *fillchar* argument.\n\nstr.lower()\n\n Return a copy of the string converted to lowercase.\n\n For 8-bit strings, this method is locale-dependent.\n\nstr.lstrip([chars])\n\n Return a copy of the string with leading characters removed. The\n *chars* argument is a string specifying the set of characters to be\n removed. If omitted or ``None``, the *chars* argument defaults to\n removing whitespace. The *chars* argument is not a prefix; rather,\n all combinations of its values are stripped:\n\n >>> \' spacious \'.lstrip()\n \'spacious \'\n >>> \'www.example.com\'.lstrip(\'cmowz.\')\n \'example.com\'\n\n Changed in version 2.2.2: Support for the *chars* argument.\n\nstr.partition(sep)\n\n Split the string at the first occurrence of *sep*, and return a\n 3-tuple containing the part before the separator, the separator\n itself, and the part after the separator. If the separator is not\n found, return a 3-tuple containing the string itself, followed by\n two empty strings.\n\n New in version 2.5.\n\nstr.replace(old, new[, count])\n\n Return a copy of the string with all occurrences of substring *old*\n replaced by *new*. If the optional argument *count* is given, only\n the first *count* occurrences are replaced.\n\nstr.rfind(sub[, start[, end]])\n\n Return the highest index in the string where substring *sub* is\n found, such that *sub* is contained within s[start,end]. Optional\n arguments *start* and *end* are interpreted as in slice notation.\n Return ``-1`` on failure.\n\nstr.rindex(sub[, start[, end]])\n\n Like ``rfind()`` but raises ``ValueError`` when the substring *sub*\n is not found.\n\nstr.rjust(width[, fillchar])\n\n Return the string right justified in a string of length *width*.\n Padding is done using the specified *fillchar* (default is a\n space). The original string is returned if *width* is less than\n ``len(s)``.\n\n Changed in version 2.4: Support for the *fillchar* argument.\n\nstr.rpartition(sep)\n\n Split the string at the last occurrence of *sep*, and return a\n 3-tuple containing the part before the separator, the separator\n itself, and the part after the separator. If the separator is not\n found, return a 3-tuple containing two empty strings, followed by\n the string itself.\n\n New in version 2.5.\n\nstr.rsplit([sep[, maxsplit]])\n\n Return a list of the words in the string, using *sep* as the\n delimiter string. If *maxsplit* is given, at most *maxsplit* splits\n are done, the *rightmost* ones. If *sep* is not specified or\n ``None``, any whitespace string is a separator. Except for\n splitting from the right, ``rsplit()`` behaves like ``split()``\n which is described in detail below.\n\n New in version 2.4.\n\nstr.rstrip([chars])\n\n Return a copy of the string with trailing characters removed. The\n *chars* argument is a string specifying the set of characters to be\n removed. If omitted or ``None``, the *chars* argument defaults to\n removing whitespace. The *chars* argument is not a suffix; rather,\n all combinations of its values are stripped:\n\n >>> \' spacious \'.rstrip()\n \' spacious\'\n >>> \'mississippi\'.rstrip(\'ipz\')\n \'mississ\'\n\n Changed in version 2.2.2: Support for the *chars* argument.\n\nstr.split([sep[, maxsplit]])\n\n Return a list of the words in the string, using *sep* as the\n delimiter string. If *maxsplit* is given, at most *maxsplit*\n splits are done (thus, the list will have at most ``maxsplit+1``\n elements). If *maxsplit* is not specified, then there is no limit\n on the number of splits (all possible splits are made).\n\n If *sep* is given, consecutive delimiters are not grouped together\n and are deemed to delimit empty strings (for example,\n ``\'1,,2\'.split(\',\')`` returns ``[\'1\', \'\', \'2\']``). The *sep*\n argument may consist of multiple characters (for example,\n ``\'1<>2<>3\'.split(\'<>\')`` returns ``[\'1\', \'2\', \'3\']``). Splitting\n an empty string with a specified separator returns ``[\'\']``.\n\n If *sep* is not specified or is ``None``, a different splitting\n algorithm is applied: runs of consecutive whitespace are regarded\n as a single separator, and the result will contain no empty strings\n at the start or end if the string has leading or trailing\n whitespace. Consequently, splitting an empty string or a string\n consisting of just whitespace with a ``None`` separator returns\n ``[]``.\n\n For example, ``\' 1 2 3 \'.split()`` returns ``[\'1\', \'2\', \'3\']``,\n and ``\' 1 2 3 \'.split(None, 1)`` returns ``[\'1\', \'2 3 \']``.\n\nstr.splitlines([keepends])\n\n Return a list of the lines in the string, breaking at line\n boundaries. Line breaks are not included in the resulting list\n unless *keepends* is given and true.\n\nstr.startswith(prefix[, start[, end]])\n\n Return ``True`` if string starts with the *prefix*, otherwise\n return ``False``. *prefix* can also be a tuple of prefixes to look\n for. With optional *start*, test string beginning at that\n position. With optional *end*, stop comparing string at that\n position.\n\n Changed in version 2.5: Accept tuples as *prefix*.\n\nstr.strip([chars])\n\n Return a copy of the string with the leading and trailing\n characters removed. The *chars* argument is a string specifying the\n set of characters to be removed. If omitted or ``None``, the\n *chars* argument defaults to removing whitespace. The *chars*\n argument is not a prefix or suffix; rather, all combinations of its\n values are stripped:\n\n >>> \' spacious \'.strip()\n \'spacious\'\n >>> \'www.example.com\'.strip(\'cmowz.\')\n \'example\'\n\n Changed in version 2.2.2: Support for the *chars* argument.\n\nstr.swapcase()\n\n Return a copy of the string with uppercase characters converted to\n lowercase and vice versa.\n\n For 8-bit strings, this method is locale-dependent.\n\nstr.title()\n\n Return a titlecased version of the string where words start with an\n uppercase character and the remaining characters are lowercase.\n\n The algorithm uses a simple language-independent definition of a\n word as groups of consecutive letters. The definition works in\n many contexts but it means that apostrophes in contractions and\n possessives form word boundaries, which may not be the desired\n result:\n\n >>> "they\'re bill\'s friends from the UK".title()\n "They\'Re Bill\'S Friends From The Uk"\n\n A workaround for apostrophes can be constructed using regular\n expressions:\n\n >>> import re\n >>> def titlecase(s):\n return re.sub(r"[A-Za-z]+(\'[A-Za-z]+)?",\n lambda mo: mo.group(0)[0].upper() +\n mo.group(0)[1:].lower(),\n s)\n\n >>> titlecase("they\'re bill\'s friends.")\n "They\'re Bill\'s Friends."\n\n For 8-bit strings, this method is locale-dependent.\n\nstr.translate(table[, deletechars])\n\n Return a copy of the string where all characters occurring in the\n optional argument *deletechars* are removed, and the remaining\n characters have been mapped through the given translation table,\n which must be a string of length 256.\n\n You can use the ``maketrans()`` helper function in the ``string``\n module to create a translation table. For string objects, set the\n *table* argument to ``None`` for translations that only delete\n characters:\n\n >>> \'read this short text\'.translate(None, \'aeiou\')\n \'rd ths shrt txt\'\n\n New in version 2.6: Support for a ``None`` *table* argument.\n\n For Unicode objects, the ``translate()`` method does not accept the\n optional *deletechars* argument. Instead, it returns a copy of the\n *s* where all characters have been mapped through the given\n translation table which must be a mapping of Unicode ordinals to\n Unicode ordinals, Unicode strings or ``None``. Unmapped characters\n are left untouched. Characters mapped to ``None`` are deleted.\n Note, a more flexible approach is to create a custom character\n mapping codec using the ``codecs`` module (see ``encodings.cp1251``\n for an example).\n\nstr.upper()\n\n Return a copy of the string converted to uppercase.\n\n For 8-bit strings, this method is locale-dependent.\n\nstr.zfill(width)\n\n Return the numeric string left filled with zeros in a string of\n length *width*. A sign prefix is handled correctly. The original\n string is returned if *width* is less than ``len(s)``.\n\n New in version 2.2.2.\n\nThe following methods are present only on unicode objects:\n\nunicode.isnumeric()\n\n Return ``True`` if there are only numeric characters in S,\n ``False`` otherwise. Numeric characters include digit characters,\n and all characters that have the Unicode numeric value property,\n e.g. U+2155, VULGAR FRACTION ONE FIFTH.\n\nunicode.isdecimal()\n\n Return ``True`` if there are only decimal characters in S,\n ``False`` otherwise. Decimal characters include digit characters,\n and all characters that that can be used to form decimal-radix\n numbers, e.g. U+0660, ARABIC-INDIC DIGIT ZERO.\n\n\nString Formatting Operations\n============================\n\nString and Unicode objects have one unique built-in operation: the\n``%`` operator (modulo). This is also known as the string\n*formatting* or *interpolation* operator. Given ``format % values``\n(where *format* is a string or Unicode object), ``%`` conversion\nspecifications in *format* are replaced with zero or more elements of\n*values*. The effect is similar to the using ``sprintf()`` in the C\nlanguage. If *format* is a Unicode object, or if any of the objects\nbeing converted using the ``%s`` conversion are Unicode objects, the\nresult will also be a Unicode object.\n\nIf *format* requires a single argument, *values* may be a single non-\ntuple object. [4] Otherwise, *values* must be a tuple with exactly\nthe number of items specified by the format string, or a single\nmapping object (for example, a dictionary).\n\nA conversion specifier contains two or more characters and has the\nfollowing components, which must occur in this order:\n\n1. The ``\'%\'`` character, which marks the start of the specifier.\n\n2. Mapping key (optional), consisting of a parenthesised sequence of\n characters (for example, ``(somename)``).\n\n3. Conversion flags (optional), which affect the result of some\n conversion types.\n\n4. Minimum field width (optional). If specified as an ``\'*\'``\n (asterisk), the actual width is read from the next element of the\n tuple in *values*, and the object to convert comes after the\n minimum field width and optional precision.\n\n5. Precision (optional), given as a ``\'.\'`` (dot) followed by the\n precision. If specified as ``\'*\'`` (an asterisk), the actual width\n is read from the next element of the tuple in *values*, and the\n value to convert comes after the precision.\n\n6. Length modifier (optional).\n\n7. Conversion type.\n\nWhen the right argument is a dictionary (or other mapping type), then\nthe formats in the string *must* include a parenthesised mapping key\ninto that dictionary inserted immediately after the ``\'%\'`` character.\nThe mapping key selects the value to be formatted from the mapping.\nFor example:\n\n>>> print \'%(language)s has %(#)03d quote types.\' % \\\n... {\'language\': "Python", "#": 2}\nPython has 002 quote types.\n\nIn this case no ``*`` specifiers may occur in a format (since they\nrequire a sequential parameter list).\n\nThe conversion flag characters are:\n\n+-----------+-----------------------------------------------------------------------+\n| Flag | Meaning |\n+===========+=======================================================================+\n| ``\'#\'`` | The value conversion will use the "alternate form" (where defined |\n| | below). |\n+-----------+-----------------------------------------------------------------------+\n| ``\'0\'`` | The conversion will be zero padded for numeric values. |\n+-----------+-----------------------------------------------------------------------+\n| ``\'-\'`` | The converted value is left adjusted (overrides the ``\'0\'`` |\n| | conversion if both are given). |\n+-----------+-----------------------------------------------------------------------+\n| ``\' \'`` | (a space) A blank should be left before a positive number (or empty |\n| | string) produced by a signed conversion. |\n+-----------+-----------------------------------------------------------------------+\n| ``\'+\'`` | A sign character (``\'+\'`` or ``\'-\'``) will precede the conversion |\n| | (overrides a "space" flag). |\n+-----------+-----------------------------------------------------------------------+\n\nA length modifier (``h``, ``l``, or ``L``) may be present, but is\nignored as it is not necessary for Python -- so e.g. ``%ld`` is\nidentical to ``%d``.\n\nThe conversion types are:\n\n+--------------+-------------------------------------------------------+---------+\n| Conversion | Meaning | Notes |\n+==============+=======================================================+=========+\n| ``\'d\'`` | Signed integer decimal. | |\n+--------------+-------------------------------------------------------+---------+\n| ``\'i\'`` | Signed integer decimal. | |\n+--------------+-------------------------------------------------------+---------+\n| ``\'o\'`` | Signed octal value. | (1) |\n+--------------+-------------------------------------------------------+---------+\n| ``\'u\'`` | Obsolete type -- it is identical to ``\'d\'``. | (7) |\n+--------------+-------------------------------------------------------+---------+\n| ``\'x\'`` | Signed hexadecimal (lowercase). | (2) |\n+--------------+-------------------------------------------------------+---------+\n| ``\'X\'`` | Signed hexadecimal (uppercase). | (2) |\n+--------------+-------------------------------------------------------+---------+\n| ``\'e\'`` | Floating point exponential format (lowercase). | (3) |\n+--------------+-------------------------------------------------------+---------+\n| ``\'E\'`` | Floating point exponential format (uppercase). | (3) |\n+--------------+-------------------------------------------------------+---------+\n| ``\'f\'`` | Floating point decimal format. | (3) |\n+--------------+-------------------------------------------------------+---------+\n| ``\'F\'`` | Floating point decimal format. | (3) |\n+--------------+-------------------------------------------------------+---------+\n| ``\'g\'`` | Floating point format. Uses lowercase exponential | (4) |\n| | format if exponent is less than -4 or not less than | |\n| | precision, decimal format otherwise. | |\n+--------------+-------------------------------------------------------+---------+\n| ``\'G\'`` | Floating point format. Uses uppercase exponential | (4) |\n| | format if exponent is less than -4 or not less than | |\n| | precision, decimal format otherwise. | |\n+--------------+-------------------------------------------------------+---------+\n| ``\'c\'`` | Single character (accepts integer or single character | |\n| | string). | |\n+--------------+-------------------------------------------------------+---------+\n| ``\'r\'`` | String (converts any Python object using ``repr()``). | (5) |\n+--------------+-------------------------------------------------------+---------+\n| ``\'s\'`` | String (converts any Python object using ``str()``). | (6) |\n+--------------+-------------------------------------------------------+---------+\n| ``\'%\'`` | No argument is converted, results in a ``\'%\'`` | |\n| | character in the result. | |\n+--------------+-------------------------------------------------------+---------+\n\nNotes:\n\n1. The alternate form causes a leading zero (``\'0\'``) to be inserted\n between left-hand padding and the formatting of the number if the\n leading character of the result is not already a zero.\n\n2. The alternate form causes a leading ``\'0x\'`` or ``\'0X\'`` (depending\n on whether the ``\'x\'`` or ``\'X\'`` format was used) to be inserted\n between left-hand padding and the formatting of the number if the\n leading character of the result is not already a zero.\n\n3. The alternate form causes the result to always contain a decimal\n point, even if no digits follow it.\n\n The precision determines the number of digits after the decimal\n point and defaults to 6.\n\n4. The alternate form causes the result to always contain a decimal\n point, and trailing zeroes are not removed as they would otherwise\n be.\n\n The precision determines the number of significant digits before\n and after the decimal point and defaults to 6.\n\n5. The ``%r`` conversion was added in Python 2.0.\n\n The precision determines the maximal number of characters used.\n\n6. If the object or format provided is a ``unicode`` string, the\n resulting string will also be ``unicode``.\n\n The precision determines the maximal number of characters used.\n\n7. See **PEP 237**.\n\nSince Python strings have an explicit length, ``%s`` conversions do\nnot assume that ``\'\\0\'`` is the end of the string.\n\nFor safety reasons, floating point precisions are clipped to 50;\n``%f`` conversions for numbers whose absolute value is over 1e50 are\nreplaced by ``%g`` conversions. [5] All other errors raise\nexceptions.\n\nAdditional string operations are defined in standard modules\n``string`` and ``re``.\n\n\nXRange Type\n===========\n\nThe ``xrange`` type is an immutable sequence which is commonly used\nfor looping. The advantage of the ``xrange`` type is that an\n``xrange`` object will always take the same amount of memory, no\nmatter the size of the range it represents. There are no consistent\nperformance advantages.\n\nXRange objects have very little behavior: they only support indexing,\niteration, and the ``len()`` function.\n\n\nMutable Sequence Types\n======================\n\nList objects support additional operations that allow in-place\nmodification of the object. Other mutable sequence types (when added\nto the language) should also support these operations. Strings and\ntuples are immutable sequence types: such objects cannot be modified\nonce created. The following operations are defined on mutable sequence\ntypes (where *x* is an arbitrary object):\n\n+--------------------------------+----------------------------------+-----------------------+\n| Operation | Result | Notes |\n+================================+==================================+=======================+\n| ``s[i] = x`` | item *i* of *s* is replaced by | |\n| | *x* | |\n+--------------------------------+----------------------------------+-----------------------+\n| ``s[i:j] = t`` | slice of *s* from *i* to *j* is | |\n| | replaced by the contents of the | |\n| | iterable *t* | |\n+--------------------------------+----------------------------------+-----------------------+\n| ``del s[i:j]`` | same as ``s[i:j] = []`` | |\n+--------------------------------+----------------------------------+-----------------------+\n| ``s[i:j:k] = t`` | the elements of ``s[i:j:k]`` are | (1) |\n| | replaced by those of *t* | |\n+--------------------------------+----------------------------------+-----------------------+\n| ``del s[i:j:k]`` | removes the elements of | |\n| | ``s[i:j:k]`` from the list | |\n+--------------------------------+----------------------------------+-----------------------+\n| ``s.append(x)`` | same as ``s[len(s):len(s)] = | (2) |\n| | [x]`` | |\n+--------------------------------+----------------------------------+-----------------------+\n| ``s.extend(x)`` | same as ``s[len(s):len(s)] = x`` | (3) |\n+--------------------------------+----------------------------------+-----------------------+\n| ``s.count(x)`` | return number of *i*\'s for which | |\n| | ``s[i] == x`` | |\n+--------------------------------+----------------------------------+-----------------------+\n| ``s.index(x[, i[, j]])`` | return smallest *k* such that | (4) |\n| | ``s[k] == x`` and ``i <= k < j`` | |\n+--------------------------------+----------------------------------+-----------------------+\n| ``s.insert(i, x)`` | same as ``s[i:i] = [x]`` | (5) |\n+--------------------------------+----------------------------------+-----------------------+\n| ``s.pop([i])`` | same as ``x = s[i]; del s[i]; | (6) |\n| | return x`` | |\n+--------------------------------+----------------------------------+-----------------------+\n| ``s.remove(x)`` | same as ``del s[s.index(x)]`` | (4) |\n+--------------------------------+----------------------------------+-----------------------+\n| ``s.reverse()`` | reverses the items of *s* in | (7) |\n| | place | |\n+--------------------------------+----------------------------------+-----------------------+\n| ``s.sort([cmp[, key[, | sort the items of *s* in place | (7)(8)(9)(10) |\n| reverse]]])`` | | |\n+--------------------------------+----------------------------------+-----------------------+\n\nNotes:\n\n1. *t* must have the same length as the slice it is replacing.\n\n2. The C implementation of Python has historically accepted multiple\n parameters and implicitly joined them into a tuple; this no longer\n works in Python 2.0. Use of this misfeature has been deprecated\n since Python 1.4.\n\n3. *x* can be any iterable object.\n\n4. Raises ``ValueError`` when *x* is not found in *s*. When a negative\n index is passed as the second or third parameter to the ``index()``\n method, the list length is added, as for slice indices. If it is\n still negative, it is truncated to zero, as for slice indices.\n\n Changed in version 2.3: Previously, ``index()`` didn\'t have\n arguments for specifying start and stop positions.\n\n5. When a negative index is passed as the first parameter to the\n ``insert()`` method, the list length is added, as for slice\n indices. If it is still negative, it is truncated to zero, as for\n slice indices.\n\n Changed in version 2.3: Previously, all negative indices were\n truncated to zero.\n\n6. The ``pop()`` method is only supported by the list and array types.\n The optional argument *i* defaults to ``-1``, so that by default\n the last item is removed and returned.\n\n7. The ``sort()`` and ``reverse()`` methods modify the list in place\n for economy of space when sorting or reversing a large list. To\n remind you that they operate by side effect, they don\'t return the\n sorted or reversed list.\n\n8. The ``sort()`` method takes optional arguments for controlling the\n comparisons.\n\n *cmp* specifies a custom comparison function of two arguments (list\n items) which should return a negative, zero or positive number\n depending on whether the first argument is considered smaller than,\n equal to, or larger than the second argument: ``cmp=lambda x,y:\n cmp(x.lower(), y.lower())``. The default value is ``None``.\n\n *key* specifies a function of one argument that is used to extract\n a comparison key from each list element: ``key=str.lower``. The\n default value is ``None``.\n\n *reverse* is a boolean value. If set to ``True``, then the list\n elements are sorted as if each comparison were reversed.\n\n In general, the *key* and *reverse* conversion processes are much\n faster than specifying an equivalent *cmp* function. This is\n because *cmp* is called multiple times for each list element while\n *key* and *reverse* touch each element only once.\n\n Changed in version 2.3: Support for ``None`` as an equivalent to\n omitting *cmp* was added.\n\n Changed in version 2.4: Support for *key* and *reverse* was added.\n\n9. Starting with Python 2.3, the ``sort()`` method is guaranteed to be\n stable. A sort is stable if it guarantees not to change the\n relative order of elements that compare equal --- this is helpful\n for sorting in multiple passes (for example, sort by department,\n then by salary grade).\n\n10. **CPython implementation detail:** While a list is being sorted,\n the effect of attempting to mutate, or even inspect, the list is\n undefined. The C implementation of Python 2.3 and newer makes the\n list appear empty for the duration, and raises ``ValueError`` if\n it can detect that the list has been mutated during a sort.\n', + 'typesseq': u'\nSequence Types --- ``str``, ``unicode``, ``list``, ``tuple``, ``buffer``, ``xrange``\n************************************************************************************\n\nThere are six sequence types: strings, Unicode strings, lists, tuples,\nbuffers, and xrange objects.\n\nFor other containers see the built in ``dict`` and ``set`` classes,\nand the ``collections`` module.\n\nString literals are written in single or double quotes: ``\'xyzzy\'``,\n``"frobozz"``. See *String literals* for more about string literals.\nUnicode strings are much like strings, but are specified in the syntax\nusing a preceding ``\'u\'`` character: ``u\'abc\'``, ``u"def"``. In\naddition to the functionality described here, there are also string-\nspecific methods described in the *String Methods* section. Lists are\nconstructed with square brackets, separating items with commas: ``[a,\nb, c]``. Tuples are constructed by the comma operator (not within\nsquare brackets), with or without enclosing parentheses, but an empty\ntuple must have the enclosing parentheses, such as ``a, b, c`` or\n``()``. A single item tuple must have a trailing comma, such as\n``(d,)``.\n\nBuffer objects are not directly supported by Python syntax, but can be\ncreated by calling the built-in function ``buffer()``. They don\'t\nsupport concatenation or repetition.\n\nObjects of type xrange are similar to buffers in that there is no\nspecific syntax to create them, but they are created using the\n``xrange()`` function. They don\'t support slicing, concatenation or\nrepetition, and using ``in``, ``not in``, ``min()`` or ``max()`` on\nthem is inefficient.\n\nMost sequence types support the following operations. The ``in`` and\n``not in`` operations have the same priorities as the comparison\noperations. The ``+`` and ``*`` operations have the same priority as\nthe corresponding numeric operations. [3] Additional methods are\nprovided for *Mutable Sequence Types*.\n\nThis table lists the sequence operations sorted in ascending priority\n(operations in the same box have the same priority). In the table,\n*s* and *t* are sequences of the same type; *n*, *i* and *j* are\nintegers:\n\n+--------------------+----------------------------------+------------+\n| Operation | Result | Notes |\n+====================+==================================+============+\n| ``x in s`` | ``True`` if an item of *s* is | (1) |\n| | equal to *x*, else ``False`` | |\n+--------------------+----------------------------------+------------+\n| ``x not in s`` | ``False`` if an item of *s* is | (1) |\n| | equal to *x*, else ``True`` | |\n+--------------------+----------------------------------+------------+\n| ``s + t`` | the concatenation of *s* and *t* | (6) |\n+--------------------+----------------------------------+------------+\n| ``s * n, n * s`` | *n* shallow copies of *s* | (2) |\n| | concatenated | |\n+--------------------+----------------------------------+------------+\n| ``s[i]`` | *i*\'th item of *s*, origin 0 | (3) |\n+--------------------+----------------------------------+------------+\n| ``s[i:j]`` | slice of *s* from *i* to *j* | (3)(4) |\n+--------------------+----------------------------------+------------+\n| ``s[i:j:k]`` | slice of *s* from *i* to *j* | (3)(5) |\n| | with step *k* | |\n+--------------------+----------------------------------+------------+\n| ``len(s)`` | length of *s* | |\n+--------------------+----------------------------------+------------+\n| ``min(s)`` | smallest item of *s* | |\n+--------------------+----------------------------------+------------+\n| ``max(s)`` | largest item of *s* | |\n+--------------------+----------------------------------+------------+\n\nSequence types also support comparisons. In particular, tuples and\nlists are compared lexicographically by comparing corresponding\nelements. This means that to compare equal, every element must compare\nequal and the two sequences must be of the same type and have the same\nlength. (For full details see *Comparisons* in the language\nreference.)\n\nNotes:\n\n1. When *s* is a string or Unicode string object the ``in`` and ``not\n in`` operations act like a substring test. In Python versions\n before 2.3, *x* had to be a string of length 1. In Python 2.3 and\n beyond, *x* may be a string of any length.\n\n2. Values of *n* less than ``0`` are treated as ``0`` (which yields an\n empty sequence of the same type as *s*). Note also that the copies\n are shallow; nested structures are not copied. This often haunts\n new Python programmers; consider:\n\n >>> lists = [[]] * 3\n >>> lists\n [[], [], []]\n >>> lists[0].append(3)\n >>> lists\n [[3], [3], [3]]\n\n What has happened is that ``[[]]`` is a one-element list containing\n an empty list, so all three elements of ``[[]] * 3`` are (pointers\n to) this single empty list. Modifying any of the elements of\n ``lists`` modifies this single list. You can create a list of\n different lists this way:\n\n >>> lists = [[] for i in range(3)]\n >>> lists[0].append(3)\n >>> lists[1].append(5)\n >>> lists[2].append(7)\n >>> lists\n [[3], [5], [7]]\n\n3. If *i* or *j* is negative, the index is relative to the end of the\n string: ``len(s) + i`` or ``len(s) + j`` is substituted. But note\n that ``-0`` is still ``0``.\n\n4. The slice of *s* from *i* to *j* is defined as the sequence of\n items with index *k* such that ``i <= k < j``. If *i* or *j* is\n greater than ``len(s)``, use ``len(s)``. If *i* is omitted or\n ``None``, use ``0``. If *j* is omitted or ``None``, use\n ``len(s)``. If *i* is greater than or equal to *j*, the slice is\n empty.\n\n5. The slice of *s* from *i* to *j* with step *k* is defined as the\n sequence of items with index ``x = i + n*k`` such that ``0 <= n <\n (j-i)/k``. In other words, the indices are ``i``, ``i+k``,\n ``i+2*k``, ``i+3*k`` and so on, stopping when *j* is reached (but\n never including *j*). If *i* or *j* is greater than ``len(s)``,\n use ``len(s)``. If *i* or *j* are omitted or ``None``, they become\n "end" values (which end depends on the sign of *k*). Note, *k*\n cannot be zero. If *k* is ``None``, it is treated like ``1``.\n\n6. **CPython implementation detail:** If *s* and *t* are both strings,\n some Python implementations such as CPython can usually perform an\n in-place optimization for assignments of the form ``s = s + t`` or\n ``s += t``. When applicable, this optimization makes quadratic\n run-time much less likely. This optimization is both version and\n implementation dependent. For performance sensitive code, it is\n preferable to use the ``str.join()`` method which assures\n consistent linear concatenation performance across versions and\n implementations.\n\n Changed in version 2.4: Formerly, string concatenation never\n occurred in-place.\n\n\nString Methods\n==============\n\nBelow are listed the string methods which both 8-bit strings and\nUnicode objects support. Note that none of these methods take keyword\narguments.\n\nIn addition, Python\'s strings support the sequence type methods\ndescribed in the *Sequence Types --- str, unicode, list, tuple,\nbuffer, xrange* section. To output formatted strings use template\nstrings or the ``%`` operator described in the *String Formatting\nOperations* section. Also, see the ``re`` module for string functions\nbased on regular expressions.\n\nstr.capitalize()\n\n Return a copy of the string with its first character capitalized\n and the rest lowercased.\n\n For 8-bit strings, this method is locale-dependent.\n\nstr.center(width[, fillchar])\n\n Return centered in a string of length *width*. Padding is done\n using the specified *fillchar* (default is a space).\n\n Changed in version 2.4: Support for the *fillchar* argument.\n\nstr.count(sub[, start[, end]])\n\n Return the number of non-overlapping occurrences of substring *sub*\n in the range [*start*, *end*]. Optional arguments *start* and\n *end* are interpreted as in slice notation.\n\nstr.decode([encoding[, errors]])\n\n Decodes the string using the codec registered for *encoding*.\n *encoding* defaults to the default string encoding. *errors* may\n be given to set a different error handling scheme. The default is\n ``\'strict\'``, meaning that encoding errors raise ``UnicodeError``.\n Other possible values are ``\'ignore\'``, ``\'replace\'`` and any other\n name registered via ``codecs.register_error()``, see section *Codec\n Base Classes*.\n\n New in version 2.2.\n\n Changed in version 2.3: Support for other error handling schemes\n added.\n\nstr.encode([encoding[, errors]])\n\n Return an encoded version of the string. Default encoding is the\n current default string encoding. *errors* may be given to set a\n different error handling scheme. The default for *errors* is\n ``\'strict\'``, meaning that encoding errors raise a\n ``UnicodeError``. Other possible values are ``\'ignore\'``,\n ``\'replace\'``, ``\'xmlcharrefreplace\'``, ``\'backslashreplace\'`` and\n any other name registered via ``codecs.register_error()``, see\n section *Codec Base Classes*. For a list of possible encodings, see\n section *Standard Encodings*.\n\n New in version 2.0.\n\n Changed in version 2.3: Support for ``\'xmlcharrefreplace\'`` and\n ``\'backslashreplace\'`` and other error handling schemes added.\n\nstr.endswith(suffix[, start[, end]])\n\n Return ``True`` if the string ends with the specified *suffix*,\n otherwise return ``False``. *suffix* can also be a tuple of\n suffixes to look for. With optional *start*, test beginning at\n that position. With optional *end*, stop comparing at that\n position.\n\n Changed in version 2.5: Accept tuples as *suffix*.\n\nstr.expandtabs([tabsize])\n\n Return a copy of the string where all tab characters are replaced\n by one or more spaces, depending on the current column and the\n given tab size. The column number is reset to zero after each\n newline occurring in the string. If *tabsize* is not given, a tab\n size of ``8`` characters is assumed. This doesn\'t understand other\n non-printing characters or escape sequences.\n\nstr.find(sub[, start[, end]])\n\n Return the lowest index in the string where substring *sub* is\n found, such that *sub* is contained in the slice ``s[start:end]``.\n Optional arguments *start* and *end* are interpreted as in slice\n notation. Return ``-1`` if *sub* is not found.\n\nstr.format(*args, **kwargs)\n\n Perform a string formatting operation. The string on which this\n method is called can contain literal text or replacement fields\n delimited by braces ``{}``. Each replacement field contains either\n the numeric index of a positional argument, or the name of a\n keyword argument. Returns a copy of the string where each\n replacement field is replaced with the string value of the\n corresponding argument.\n\n >>> "The sum of 1 + 2 is {0}".format(1+2)\n \'The sum of 1 + 2 is 3\'\n\n See *Format String Syntax* for a description of the various\n formatting options that can be specified in format strings.\n\n This method of string formatting is the new standard in Python 3.0,\n and should be preferred to the ``%`` formatting described in\n *String Formatting Operations* in new code.\n\n New in version 2.6.\n\nstr.index(sub[, start[, end]])\n\n Like ``find()``, but raise ``ValueError`` when the substring is not\n found.\n\nstr.isalnum()\n\n Return true if all characters in the string are alphanumeric and\n there is at least one character, false otherwise.\n\n For 8-bit strings, this method is locale-dependent.\n\nstr.isalpha()\n\n Return true if all characters in the string are alphabetic and\n there is at least one character, false otherwise.\n\n For 8-bit strings, this method is locale-dependent.\n\nstr.isdigit()\n\n Return true if all characters in the string are digits and there is\n at least one character, false otherwise.\n\n For 8-bit strings, this method is locale-dependent.\n\nstr.islower()\n\n Return true if all cased characters in the string are lowercase and\n there is at least one cased character, false otherwise.\n\n For 8-bit strings, this method is locale-dependent.\n\nstr.isspace()\n\n Return true if there are only whitespace characters in the string\n and there is at least one character, false otherwise.\n\n For 8-bit strings, this method is locale-dependent.\n\nstr.istitle()\n\n Return true if the string is a titlecased string and there is at\n least one character, for example uppercase characters may only\n follow uncased characters and lowercase characters only cased ones.\n Return false otherwise.\n\n For 8-bit strings, this method is locale-dependent.\n\nstr.isupper()\n\n Return true if all cased characters in the string are uppercase and\n there is at least one cased character, false otherwise.\n\n For 8-bit strings, this method is locale-dependent.\n\nstr.join(iterable)\n\n Return a string which is the concatenation of the strings in the\n *iterable* *iterable*. The separator between elements is the\n string providing this method.\n\nstr.ljust(width[, fillchar])\n\n Return the string left justified in a string of length *width*.\n Padding is done using the specified *fillchar* (default is a\n space). The original string is returned if *width* is less than\n ``len(s)``.\n\n Changed in version 2.4: Support for the *fillchar* argument.\n\nstr.lower()\n\n Return a copy of the string converted to lowercase.\n\n For 8-bit strings, this method is locale-dependent.\n\nstr.lstrip([chars])\n\n Return a copy of the string with leading characters removed. The\n *chars* argument is a string specifying the set of characters to be\n removed. If omitted or ``None``, the *chars* argument defaults to\n removing whitespace. The *chars* argument is not a prefix; rather,\n all combinations of its values are stripped:\n\n >>> \' spacious \'.lstrip()\n \'spacious \'\n >>> \'www.example.com\'.lstrip(\'cmowz.\')\n \'example.com\'\n\n Changed in version 2.2.2: Support for the *chars* argument.\n\nstr.partition(sep)\n\n Split the string at the first occurrence of *sep*, and return a\n 3-tuple containing the part before the separator, the separator\n itself, and the part after the separator. If the separator is not\n found, return a 3-tuple containing the string itself, followed by\n two empty strings.\n\n New in version 2.5.\n\nstr.replace(old, new[, count])\n\n Return a copy of the string with all occurrences of substring *old*\n replaced by *new*. If the optional argument *count* is given, only\n the first *count* occurrences are replaced.\n\nstr.rfind(sub[, start[, end]])\n\n Return the highest index in the string where substring *sub* is\n found, such that *sub* is contained within ``s[start:end]``.\n Optional arguments *start* and *end* are interpreted as in slice\n notation. Return ``-1`` on failure.\n\nstr.rindex(sub[, start[, end]])\n\n Like ``rfind()`` but raises ``ValueError`` when the substring *sub*\n is not found.\n\nstr.rjust(width[, fillchar])\n\n Return the string right justified in a string of length *width*.\n Padding is done using the specified *fillchar* (default is a\n space). The original string is returned if *width* is less than\n ``len(s)``.\n\n Changed in version 2.4: Support for the *fillchar* argument.\n\nstr.rpartition(sep)\n\n Split the string at the last occurrence of *sep*, and return a\n 3-tuple containing the part before the separator, the separator\n itself, and the part after the separator. If the separator is not\n found, return a 3-tuple containing two empty strings, followed by\n the string itself.\n\n New in version 2.5.\n\nstr.rsplit([sep[, maxsplit]])\n\n Return a list of the words in the string, using *sep* as the\n delimiter string. If *maxsplit* is given, at most *maxsplit* splits\n are done, the *rightmost* ones. If *sep* is not specified or\n ``None``, any whitespace string is a separator. Except for\n splitting from the right, ``rsplit()`` behaves like ``split()``\n which is described in detail below.\n\n New in version 2.4.\n\nstr.rstrip([chars])\n\n Return a copy of the string with trailing characters removed. The\n *chars* argument is a string specifying the set of characters to be\n removed. If omitted or ``None``, the *chars* argument defaults to\n removing whitespace. The *chars* argument is not a suffix; rather,\n all combinations of its values are stripped:\n\n >>> \' spacious \'.rstrip()\n \' spacious\'\n >>> \'mississippi\'.rstrip(\'ipz\')\n \'mississ\'\n\n Changed in version 2.2.2: Support for the *chars* argument.\n\nstr.split([sep[, maxsplit]])\n\n Return a list of the words in the string, using *sep* as the\n delimiter string. If *maxsplit* is given, at most *maxsplit*\n splits are done (thus, the list will have at most ``maxsplit+1``\n elements). If *maxsplit* is not specified, then there is no limit\n on the number of splits (all possible splits are made).\n\n If *sep* is given, consecutive delimiters are not grouped together\n and are deemed to delimit empty strings (for example,\n ``\'1,,2\'.split(\',\')`` returns ``[\'1\', \'\', \'2\']``). The *sep*\n argument may consist of multiple characters (for example,\n ``\'1<>2<>3\'.split(\'<>\')`` returns ``[\'1\', \'2\', \'3\']``). Splitting\n an empty string with a specified separator returns ``[\'\']``.\n\n If *sep* is not specified or is ``None``, a different splitting\n algorithm is applied: runs of consecutive whitespace are regarded\n as a single separator, and the result will contain no empty strings\n at the start or end if the string has leading or trailing\n whitespace. Consequently, splitting an empty string or a string\n consisting of just whitespace with a ``None`` separator returns\n ``[]``.\n\n For example, ``\' 1 2 3 \'.split()`` returns ``[\'1\', \'2\', \'3\']``,\n and ``\' 1 2 3 \'.split(None, 1)`` returns ``[\'1\', \'2 3 \']``.\n\nstr.splitlines([keepends])\n\n Return a list of the lines in the string, breaking at line\n boundaries. Line breaks are not included in the resulting list\n unless *keepends* is given and true.\n\nstr.startswith(prefix[, start[, end]])\n\n Return ``True`` if string starts with the *prefix*, otherwise\n return ``False``. *prefix* can also be a tuple of prefixes to look\n for. With optional *start*, test string beginning at that\n position. With optional *end*, stop comparing string at that\n position.\n\n Changed in version 2.5: Accept tuples as *prefix*.\n\nstr.strip([chars])\n\n Return a copy of the string with the leading and trailing\n characters removed. The *chars* argument is a string specifying the\n set of characters to be removed. If omitted or ``None``, the\n *chars* argument defaults to removing whitespace. The *chars*\n argument is not a prefix or suffix; rather, all combinations of its\n values are stripped:\n\n >>> \' spacious \'.strip()\n \'spacious\'\n >>> \'www.example.com\'.strip(\'cmowz.\')\n \'example\'\n\n Changed in version 2.2.2: Support for the *chars* argument.\n\nstr.swapcase()\n\n Return a copy of the string with uppercase characters converted to\n lowercase and vice versa.\n\n For 8-bit strings, this method is locale-dependent.\n\nstr.title()\n\n Return a titlecased version of the string where words start with an\n uppercase character and the remaining characters are lowercase.\n\n The algorithm uses a simple language-independent definition of a\n word as groups of consecutive letters. The definition works in\n many contexts but it means that apostrophes in contractions and\n possessives form word boundaries, which may not be the desired\n result:\n\n >>> "they\'re bill\'s friends from the UK".title()\n "They\'Re Bill\'S Friends From The Uk"\n\n A workaround for apostrophes can be constructed using regular\n expressions:\n\n >>> import re\n >>> def titlecase(s):\n return re.sub(r"[A-Za-z]+(\'[A-Za-z]+)?",\n lambda mo: mo.group(0)[0].upper() +\n mo.group(0)[1:].lower(),\n s)\n\n >>> titlecase("they\'re bill\'s friends.")\n "They\'re Bill\'s Friends."\n\n For 8-bit strings, this method is locale-dependent.\n\nstr.translate(table[, deletechars])\n\n Return a copy of the string where all characters occurring in the\n optional argument *deletechars* are removed, and the remaining\n characters have been mapped through the given translation table,\n which must be a string of length 256.\n\n You can use the ``maketrans()`` helper function in the ``string``\n module to create a translation table. For string objects, set the\n *table* argument to ``None`` for translations that only delete\n characters:\n\n >>> \'read this short text\'.translate(None, \'aeiou\')\n \'rd ths shrt txt\'\n\n New in version 2.6: Support for a ``None`` *table* argument.\n\n For Unicode objects, the ``translate()`` method does not accept the\n optional *deletechars* argument. Instead, it returns a copy of the\n *s* where all characters have been mapped through the given\n translation table which must be a mapping of Unicode ordinals to\n Unicode ordinals, Unicode strings or ``None``. Unmapped characters\n are left untouched. Characters mapped to ``None`` are deleted.\n Note, a more flexible approach is to create a custom character\n mapping codec using the ``codecs`` module (see ``encodings.cp1251``\n for an example).\n\nstr.upper()\n\n Return a copy of the string converted to uppercase.\n\n For 8-bit strings, this method is locale-dependent.\n\nstr.zfill(width)\n\n Return the numeric string left filled with zeros in a string of\n length *width*. A sign prefix is handled correctly. The original\n string is returned if *width* is less than ``len(s)``.\n\n New in version 2.2.2.\n\nThe following methods are present only on unicode objects:\n\nunicode.isnumeric()\n\n Return ``True`` if there are only numeric characters in S,\n ``False`` otherwise. Numeric characters include digit characters,\n and all characters that have the Unicode numeric value property,\n e.g. U+2155, VULGAR FRACTION ONE FIFTH.\n\nunicode.isdecimal()\n\n Return ``True`` if there are only decimal characters in S,\n ``False`` otherwise. Decimal characters include digit characters,\n and all characters that that can be used to form decimal-radix\n numbers, e.g. U+0660, ARABIC-INDIC DIGIT ZERO.\n\n\nString Formatting Operations\n============================\n\nString and Unicode objects have one unique built-in operation: the\n``%`` operator (modulo). This is also known as the string\n*formatting* or *interpolation* operator. Given ``format % values``\n(where *format* is a string or Unicode object), ``%`` conversion\nspecifications in *format* are replaced with zero or more elements of\n*values*. The effect is similar to the using ``sprintf()`` in the C\nlanguage. If *format* is a Unicode object, or if any of the objects\nbeing converted using the ``%s`` conversion are Unicode objects, the\nresult will also be a Unicode object.\n\nIf *format* requires a single argument, *values* may be a single non-\ntuple object. [4] Otherwise, *values* must be a tuple with exactly\nthe number of items specified by the format string, or a single\nmapping object (for example, a dictionary).\n\nA conversion specifier contains two or more characters and has the\nfollowing components, which must occur in this order:\n\n1. The ``\'%\'`` character, which marks the start of the specifier.\n\n2. Mapping key (optional), consisting of a parenthesised sequence of\n characters (for example, ``(somename)``).\n\n3. Conversion flags (optional), which affect the result of some\n conversion types.\n\n4. Minimum field width (optional). If specified as an ``\'*\'``\n (asterisk), the actual width is read from the next element of the\n tuple in *values*, and the object to convert comes after the\n minimum field width and optional precision.\n\n5. Precision (optional), given as a ``\'.\'`` (dot) followed by the\n precision. If specified as ``\'*\'`` (an asterisk), the actual width\n is read from the next element of the tuple in *values*, and the\n value to convert comes after the precision.\n\n6. Length modifier (optional).\n\n7. Conversion type.\n\nWhen the right argument is a dictionary (or other mapping type), then\nthe formats in the string *must* include a parenthesised mapping key\ninto that dictionary inserted immediately after the ``\'%\'`` character.\nThe mapping key selects the value to be formatted from the mapping.\nFor example:\n\n>>> print \'%(language)s has %(#)03d quote types.\' % \\\n... {\'language\': "Python", "#": 2}\nPython has 002 quote types.\n\nIn this case no ``*`` specifiers may occur in a format (since they\nrequire a sequential parameter list).\n\nThe conversion flag characters are:\n\n+-----------+-----------------------------------------------------------------------+\n| Flag | Meaning |\n+===========+=======================================================================+\n| ``\'#\'`` | The value conversion will use the "alternate form" (where defined |\n| | below). |\n+-----------+-----------------------------------------------------------------------+\n| ``\'0\'`` | The conversion will be zero padded for numeric values. |\n+-----------+-----------------------------------------------------------------------+\n| ``\'-\'`` | The converted value is left adjusted (overrides the ``\'0\'`` |\n| | conversion if both are given). |\n+-----------+-----------------------------------------------------------------------+\n| ``\' \'`` | (a space) A blank should be left before a positive number (or empty |\n| | string) produced by a signed conversion. |\n+-----------+-----------------------------------------------------------------------+\n| ``\'+\'`` | A sign character (``\'+\'`` or ``\'-\'``) will precede the conversion |\n| | (overrides a "space" flag). |\n+-----------+-----------------------------------------------------------------------+\n\nA length modifier (``h``, ``l``, or ``L``) may be present, but is\nignored as it is not necessary for Python -- so e.g. ``%ld`` is\nidentical to ``%d``.\n\nThe conversion types are:\n\n+--------------+-------------------------------------------------------+---------+\n| Conversion | Meaning | Notes |\n+==============+=======================================================+=========+\n| ``\'d\'`` | Signed integer decimal. | |\n+--------------+-------------------------------------------------------+---------+\n| ``\'i\'`` | Signed integer decimal. | |\n+--------------+-------------------------------------------------------+---------+\n| ``\'o\'`` | Signed octal value. | (1) |\n+--------------+-------------------------------------------------------+---------+\n| ``\'u\'`` | Obsolete type -- it is identical to ``\'d\'``. | (7) |\n+--------------+-------------------------------------------------------+---------+\n| ``\'x\'`` | Signed hexadecimal (lowercase). | (2) |\n+--------------+-------------------------------------------------------+---------+\n| ``\'X\'`` | Signed hexadecimal (uppercase). | (2) |\n+--------------+-------------------------------------------------------+---------+\n| ``\'e\'`` | Floating point exponential format (lowercase). | (3) |\n+--------------+-------------------------------------------------------+---------+\n| ``\'E\'`` | Floating point exponential format (uppercase). | (3) |\n+--------------+-------------------------------------------------------+---------+\n| ``\'f\'`` | Floating point decimal format. | (3) |\n+--------------+-------------------------------------------------------+---------+\n| ``\'F\'`` | Floating point decimal format. | (3) |\n+--------------+-------------------------------------------------------+---------+\n| ``\'g\'`` | Floating point format. Uses lowercase exponential | (4) |\n| | format if exponent is less than -4 or not less than | |\n| | precision, decimal format otherwise. | |\n+--------------+-------------------------------------------------------+---------+\n| ``\'G\'`` | Floating point format. Uses uppercase exponential | (4) |\n| | format if exponent is less than -4 or not less than | |\n| | precision, decimal format otherwise. | |\n+--------------+-------------------------------------------------------+---------+\n| ``\'c\'`` | Single character (accepts integer or single character | |\n| | string). | |\n+--------------+-------------------------------------------------------+---------+\n| ``\'r\'`` | String (converts any Python object using ``repr()``). | (5) |\n+--------------+-------------------------------------------------------+---------+\n| ``\'s\'`` | String (converts any Python object using ``str()``). | (6) |\n+--------------+-------------------------------------------------------+---------+\n| ``\'%\'`` | No argument is converted, results in a ``\'%\'`` | |\n| | character in the result. | |\n+--------------+-------------------------------------------------------+---------+\n\nNotes:\n\n1. The alternate form causes a leading zero (``\'0\'``) to be inserted\n between left-hand padding and the formatting of the number if the\n leading character of the result is not already a zero.\n\n2. The alternate form causes a leading ``\'0x\'`` or ``\'0X\'`` (depending\n on whether the ``\'x\'`` or ``\'X\'`` format was used) to be inserted\n between left-hand padding and the formatting of the number if the\n leading character of the result is not already a zero.\n\n3. The alternate form causes the result to always contain a decimal\n point, even if no digits follow it.\n\n The precision determines the number of digits after the decimal\n point and defaults to 6.\n\n4. The alternate form causes the result to always contain a decimal\n point, and trailing zeroes are not removed as they would otherwise\n be.\n\n The precision determines the number of significant digits before\n and after the decimal point and defaults to 6.\n\n5. The ``%r`` conversion was added in Python 2.0.\n\n The precision determines the maximal number of characters used.\n\n6. If the object or format provided is a ``unicode`` string, the\n resulting string will also be ``unicode``.\n\n The precision determines the maximal number of characters used.\n\n7. See **PEP 237**.\n\nSince Python strings have an explicit length, ``%s`` conversions do\nnot assume that ``\'\\0\'`` is the end of the string.\n\nFor safety reasons, floating point precisions are clipped to 50;\n``%f`` conversions for numbers whose absolute value is over 1e50 are\nreplaced by ``%g`` conversions. [5] All other errors raise\nexceptions.\n\nAdditional string operations are defined in standard modules\n``string`` and ``re``.\n\n\nXRange Type\n===========\n\nThe ``xrange`` type is an immutable sequence which is commonly used\nfor looping. The advantage of the ``xrange`` type is that an\n``xrange`` object will always take the same amount of memory, no\nmatter the size of the range it represents. There are no consistent\nperformance advantages.\n\nXRange objects have very little behavior: they only support indexing,\niteration, and the ``len()`` function.\n\n\nMutable Sequence Types\n======================\n\nList objects support additional operations that allow in-place\nmodification of the object. Other mutable sequence types (when added\nto the language) should also support these operations. Strings and\ntuples are immutable sequence types: such objects cannot be modified\nonce created. The following operations are defined on mutable sequence\ntypes (where *x* is an arbitrary object):\n\n+--------------------------------+----------------------------------+-----------------------+\n| Operation | Result | Notes |\n+================================+==================================+=======================+\n| ``s[i] = x`` | item *i* of *s* is replaced by | |\n| | *x* | |\n+--------------------------------+----------------------------------+-----------------------+\n| ``s[i:j] = t`` | slice of *s* from *i* to *j* is | |\n| | replaced by the contents of the | |\n| | iterable *t* | |\n+--------------------------------+----------------------------------+-----------------------+\n| ``del s[i:j]`` | same as ``s[i:j] = []`` | |\n+--------------------------------+----------------------------------+-----------------------+\n| ``s[i:j:k] = t`` | the elements of ``s[i:j:k]`` are | (1) |\n| | replaced by those of *t* | |\n+--------------------------------+----------------------------------+-----------------------+\n| ``del s[i:j:k]`` | removes the elements of | |\n| | ``s[i:j:k]`` from the list | |\n+--------------------------------+----------------------------------+-----------------------+\n| ``s.append(x)`` | same as ``s[len(s):len(s)] = | (2) |\n| | [x]`` | |\n+--------------------------------+----------------------------------+-----------------------+\n| ``s.extend(x)`` | same as ``s[len(s):len(s)] = x`` | (3) |\n+--------------------------------+----------------------------------+-----------------------+\n| ``s.count(x)`` | return number of *i*\'s for which | |\n| | ``s[i] == x`` | |\n+--------------------------------+----------------------------------+-----------------------+\n| ``s.index(x[, i[, j]])`` | return smallest *k* such that | (4) |\n| | ``s[k] == x`` and ``i <= k < j`` | |\n+--------------------------------+----------------------------------+-----------------------+\n| ``s.insert(i, x)`` | same as ``s[i:i] = [x]`` | (5) |\n+--------------------------------+----------------------------------+-----------------------+\n| ``s.pop([i])`` | same as ``x = s[i]; del s[i]; | (6) |\n| | return x`` | |\n+--------------------------------+----------------------------------+-----------------------+\n| ``s.remove(x)`` | same as ``del s[s.index(x)]`` | (4) |\n+--------------------------------+----------------------------------+-----------------------+\n| ``s.reverse()`` | reverses the items of *s* in | (7) |\n| | place | |\n+--------------------------------+----------------------------------+-----------------------+\n| ``s.sort([cmp[, key[, | sort the items of *s* in place | (7)(8)(9)(10) |\n| reverse]]])`` | | |\n+--------------------------------+----------------------------------+-----------------------+\n\nNotes:\n\n1. *t* must have the same length as the slice it is replacing.\n\n2. The C implementation of Python has historically accepted multiple\n parameters and implicitly joined them into a tuple; this no longer\n works in Python 2.0. Use of this misfeature has been deprecated\n since Python 1.4.\n\n3. *x* can be any iterable object.\n\n4. Raises ``ValueError`` when *x* is not found in *s*. When a negative\n index is passed as the second or third parameter to the ``index()``\n method, the list length is added, as for slice indices. If it is\n still negative, it is truncated to zero, as for slice indices.\n\n Changed in version 2.3: Previously, ``index()`` didn\'t have\n arguments for specifying start and stop positions.\n\n5. When a negative index is passed as the first parameter to the\n ``insert()`` method, the list length is added, as for slice\n indices. If it is still negative, it is truncated to zero, as for\n slice indices.\n\n Changed in version 2.3: Previously, all negative indices were\n truncated to zero.\n\n6. The ``pop()`` method is only supported by the list and array types.\n The optional argument *i* defaults to ``-1``, so that by default\n the last item is removed and returned.\n\n7. The ``sort()`` and ``reverse()`` methods modify the list in place\n for economy of space when sorting or reversing a large list. To\n remind you that they operate by side effect, they don\'t return the\n sorted or reversed list.\n\n8. The ``sort()`` method takes optional arguments for controlling the\n comparisons.\n\n *cmp* specifies a custom comparison function of two arguments (list\n items) which should return a negative, zero or positive number\n depending on whether the first argument is considered smaller than,\n equal to, or larger than the second argument: ``cmp=lambda x,y:\n cmp(x.lower(), y.lower())``. The default value is ``None``.\n\n *key* specifies a function of one argument that is used to extract\n a comparison key from each list element: ``key=str.lower``. The\n default value is ``None``.\n\n *reverse* is a boolean value. If set to ``True``, then the list\n elements are sorted as if each comparison were reversed.\n\n In general, the *key* and *reverse* conversion processes are much\n faster than specifying an equivalent *cmp* function. This is\n because *cmp* is called multiple times for each list element while\n *key* and *reverse* touch each element only once.\n\n Changed in version 2.3: Support for ``None`` as an equivalent to\n omitting *cmp* was added.\n\n Changed in version 2.4: Support for *key* and *reverse* was added.\n\n9. Starting with Python 2.3, the ``sort()`` method is guaranteed to be\n stable. A sort is stable if it guarantees not to change the\n relative order of elements that compare equal --- this is helpful\n for sorting in multiple passes (for example, sort by department,\n then by salary grade).\n\n10. **CPython implementation detail:** While a list is being sorted,\n the effect of attempting to mutate, or even inspect, the list is\n undefined. The C implementation of Python 2.3 and newer makes the\n list appear empty for the duration, and raises ``ValueError`` if\n it can detect that the list has been mutated during a sort.\n', 'typesseq-mutable': u"\nMutable Sequence Types\n**********************\n\nList objects support additional operations that allow in-place\nmodification of the object. Other mutable sequence types (when added\nto the language) should also support these operations. Strings and\ntuples are immutable sequence types: such objects cannot be modified\nonce created. The following operations are defined on mutable sequence\ntypes (where *x* is an arbitrary object):\n\n+--------------------------------+----------------------------------+-----------------------+\n| Operation | Result | Notes |\n+================================+==================================+=======================+\n| ``s[i] = x`` | item *i* of *s* is replaced by | |\n| | *x* | |\n+--------------------------------+----------------------------------+-----------------------+\n| ``s[i:j] = t`` | slice of *s* from *i* to *j* is | |\n| | replaced by the contents of the | |\n| | iterable *t* | |\n+--------------------------------+----------------------------------+-----------------------+\n| ``del s[i:j]`` | same as ``s[i:j] = []`` | |\n+--------------------------------+----------------------------------+-----------------------+\n| ``s[i:j:k] = t`` | the elements of ``s[i:j:k]`` are | (1) |\n| | replaced by those of *t* | |\n+--------------------------------+----------------------------------+-----------------------+\n| ``del s[i:j:k]`` | removes the elements of | |\n| | ``s[i:j:k]`` from the list | |\n+--------------------------------+----------------------------------+-----------------------+\n| ``s.append(x)`` | same as ``s[len(s):len(s)] = | (2) |\n| | [x]`` | |\n+--------------------------------+----------------------------------+-----------------------+\n| ``s.extend(x)`` | same as ``s[len(s):len(s)] = x`` | (3) |\n+--------------------------------+----------------------------------+-----------------------+\n| ``s.count(x)`` | return number of *i*'s for which | |\n| | ``s[i] == x`` | |\n+--------------------------------+----------------------------------+-----------------------+\n| ``s.index(x[, i[, j]])`` | return smallest *k* such that | (4) |\n| | ``s[k] == x`` and ``i <= k < j`` | |\n+--------------------------------+----------------------------------+-----------------------+\n| ``s.insert(i, x)`` | same as ``s[i:i] = [x]`` | (5) |\n+--------------------------------+----------------------------------+-----------------------+\n| ``s.pop([i])`` | same as ``x = s[i]; del s[i]; | (6) |\n| | return x`` | |\n+--------------------------------+----------------------------------+-----------------------+\n| ``s.remove(x)`` | same as ``del s[s.index(x)]`` | (4) |\n+--------------------------------+----------------------------------+-----------------------+\n| ``s.reverse()`` | reverses the items of *s* in | (7) |\n| | place | |\n+--------------------------------+----------------------------------+-----------------------+\n| ``s.sort([cmp[, key[, | sort the items of *s* in place | (7)(8)(9)(10) |\n| reverse]]])`` | | |\n+--------------------------------+----------------------------------+-----------------------+\n\nNotes:\n\n1. *t* must have the same length as the slice it is replacing.\n\n2. The C implementation of Python has historically accepted multiple\n parameters and implicitly joined them into a tuple; this no longer\n works in Python 2.0. Use of this misfeature has been deprecated\n since Python 1.4.\n\n3. *x* can be any iterable object.\n\n4. Raises ``ValueError`` when *x* is not found in *s*. When a negative\n index is passed as the second or third parameter to the ``index()``\n method, the list length is added, as for slice indices. If it is\n still negative, it is truncated to zero, as for slice indices.\n\n Changed in version 2.3: Previously, ``index()`` didn't have\n arguments for specifying start and stop positions.\n\n5. When a negative index is passed as the first parameter to the\n ``insert()`` method, the list length is added, as for slice\n indices. If it is still negative, it is truncated to zero, as for\n slice indices.\n\n Changed in version 2.3: Previously, all negative indices were\n truncated to zero.\n\n6. The ``pop()`` method is only supported by the list and array types.\n The optional argument *i* defaults to ``-1``, so that by default\n the last item is removed and returned.\n\n7. The ``sort()`` and ``reverse()`` methods modify the list in place\n for economy of space when sorting or reversing a large list. To\n remind you that they operate by side effect, they don't return the\n sorted or reversed list.\n\n8. The ``sort()`` method takes optional arguments for controlling the\n comparisons.\n\n *cmp* specifies a custom comparison function of two arguments (list\n items) which should return a negative, zero or positive number\n depending on whether the first argument is considered smaller than,\n equal to, or larger than the second argument: ``cmp=lambda x,y:\n cmp(x.lower(), y.lower())``. The default value is ``None``.\n\n *key* specifies a function of one argument that is used to extract\n a comparison key from each list element: ``key=str.lower``. The\n default value is ``None``.\n\n *reverse* is a boolean value. If set to ``True``, then the list\n elements are sorted as if each comparison were reversed.\n\n In general, the *key* and *reverse* conversion processes are much\n faster than specifying an equivalent *cmp* function. This is\n because *cmp* is called multiple times for each list element while\n *key* and *reverse* touch each element only once.\n\n Changed in version 2.3: Support for ``None`` as an equivalent to\n omitting *cmp* was added.\n\n Changed in version 2.4: Support for *key* and *reverse* was added.\n\n9. Starting with Python 2.3, the ``sort()`` method is guaranteed to be\n stable. A sort is stable if it guarantees not to change the\n relative order of elements that compare equal --- this is helpful\n for sorting in multiple passes (for example, sort by department,\n then by salary grade).\n\n10. **CPython implementation detail:** While a list is being sorted,\n the effect of attempting to mutate, or even inspect, the list is\n undefined. The C implementation of Python 2.3 and newer makes the\n list appear empty for the duration, and raises ``ValueError`` if\n it can detect that the list has been mutated during a sort.\n", 'unary': u'\nUnary arithmetic and bitwise operations\n***************************************\n\nAll unary arithmetic and bitwise operations have the same priority:\n\n u_expr ::= power | "-" u_expr | "+" u_expr | "~" u_expr\n\nThe unary ``-`` (minus) operator yields the negation of its numeric\nargument.\n\nThe unary ``+`` (plus) operator yields its numeric argument unchanged.\n\nThe unary ``~`` (invert) operator yields the bitwise inversion of its\nplain or long integer argument. The bitwise inversion of ``x`` is\ndefined as ``-(x+1)``. It only applies to integral numbers.\n\nIn all three cases, if the argument does not have the proper type, a\n``TypeError`` exception is raised.\n', 'while': u'\nThe ``while`` statement\n***********************\n\nThe ``while`` statement is used for repeated execution as long as an\nexpression is true:\n\n while_stmt ::= "while" expression ":" suite\n ["else" ":" suite]\n\nThis repeatedly tests the expression and, if it is true, executes the\nfirst suite; if the expression is false (which may be the first time\nit is tested) the suite of the ``else`` clause, if present, is\nexecuted and the loop terminates.\n\nA ``break`` statement executed in the first suite terminates the loop\nwithout executing the ``else`` clause\'s suite. A ``continue``\nstatement executed in the first suite skips the rest of the suite and\ngoes back to testing the expression.\n', Modified: python/branches/release26-maint/Misc/NEWS ============================================================================== --- python/branches/release26-maint/Misc/NEWS (original) +++ python/branches/release26-maint/Misc/NEWS Wed Aug 4 00:39:42 2010 @@ -4,10 +4,10 @@ (editors: check NEWS.help for information about editing NEWS using ReST.) -What's New in Python 2.6.6 alpha 1? -=================================== +What's New in Python 2.6.6 rc 1? +================================ -*Release date: XXXX-XX-XX* +*Release date: 2010-08-03* Core and Builtins ----------------- @@ -58,8 +58,8 @@ small arguments were treated this way; larger arguments (those whose __int__ was outside the range of a C long) would produce a TypeError. -- Issue #8417: Raise an OverflowError when an integer larger than sys.maxsize is - passed to bytearray. +- Issue #8417: Raise an OverflowError when an integer larger than sys.maxsize + is passed to bytearray. - Issue #8329: Don't return the same lists from select.select when no fds are changed. Modified: python/branches/release26-maint/Misc/RPM/python-2.6.spec ============================================================================== --- python/branches/release26-maint/Misc/RPM/python-2.6.spec (original) +++ python/branches/release26-maint/Misc/RPM/python-2.6.spec Wed Aug 4 00:39:42 2010 @@ -39,7 +39,7 @@ %define name python #--start constants-- -%define version 2.6.5 +%define version 2.6.6rc1 %define libver 2.6 #--end constants-- %define release 1pydotorg Modified: python/branches/release26-maint/README ============================================================================== --- python/branches/release26-maint/README (original) +++ python/branches/release26-maint/README Wed Aug 4 00:39:42 2010 @@ -1,5 +1,5 @@ -This is Python version 2.6.5 -============================ +This is Python version 2.6.6 release candidate 1 +================================================ Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Python Software Foundation. From python-checkins at python.org Wed Aug 4 00:51:58 2010 From: python-checkins at python.org (barry.warsaw) Date: Wed, 4 Aug 2010 00:51:58 +0200 (CEST) Subject: [Python-checkins] r83692 - python/tags/r266rc1 Message-ID: <20100803225158.20E66EE987@mail.python.org> Author: barry.warsaw Date: Wed Aug 4 00:51:57 2010 New Revision: 83692 Log: Tagging 2.6.6 rc 1 Added: python/tags/r266rc1/ - copied from r83691, /python/branches/release26-maint/ From python-checkins at python.org Wed Aug 4 01:23:28 2010 From: python-checkins at python.org (barry.warsaw) Date: Wed, 4 Aug 2010 01:23:28 +0200 (CEST) Subject: [Python-checkins] r83693 - sandbox/trunk/release/release.py Message-ID: <20100803232328.075ECEE9A9@mail.python.org> Author: barry.warsaw Date: Wed Aug 4 01:23:27 2010 New Revision: 83693 Log: Fix typo. Modified: sandbox/trunk/release/release.py Modified: sandbox/trunk/release/release.py ============================================================================== --- sandbox/trunk/release/release.py (original) +++ sandbox/trunk/release/release.py Wed Aug 4 01:23:27 2010 @@ -39,7 +39,7 @@ print('Executing %s' % cmd) try: if silent: - code = subprocess.call(cmd, shell=True, stdout=PIPE) + code = subprocess.call(cmd, shell=True, stdout=subprocess.PIPE) else: code = subprocess.call(cmd, shell=True) except OSError: From python-checkins at python.org Wed Aug 4 01:35:45 2010 From: python-checkins at python.org (r.david.murray) Date: Wed, 4 Aug 2010 01:35:45 +0200 (CEST) Subject: [Python-checkins] r83694 - in python/branches/release31-maint: Lib/email/header.py Lib/email/test/test_email.py Misc/ACKS Misc/NEWS Message-ID: <20100803233545.20C3AEE98A@mail.python.org> Author: r.david.murray Date: Wed Aug 4 01:35:44 2010 New Revision: 83694 Log: Merged revisions 83690 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83690 | r.david.murray | 2010-08-03 18:14:10 -0400 (Tue, 03 Aug 2010) | 10 lines #3196: if needed pad a short base64 encoded word before trying to decode. The RFCs encourage following Postel's law: be liberal in what you accept. So if someone forgot to pad the base64 encoded word payload to an even four bytes, we add the padding before handing it to base64mime.decode. Previously, missing padding resulted in a HeaderParseError. Patch by Jason Williams. ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Lib/email/header.py python/branches/release31-maint/Lib/email/test/test_email.py python/branches/release31-maint/Misc/ACKS python/branches/release31-maint/Misc/NEWS Modified: python/branches/release31-maint/Lib/email/header.py ============================================================================== --- python/branches/release31-maint/Lib/email/header.py (original) +++ python/branches/release31-maint/Lib/email/header.py Wed Aug 4 01:35:44 2010 @@ -94,6 +94,9 @@ word = email.quoprimime.header_decode(encoded_string) decoded_words.append((word, charset)) elif encoding == 'b': + paderr = len(encoded_string) % 4 # Postel's law: add missing padding + if paderr: + encoded_string += '==='[:4 - paderr] try: word = email.base64mime.decode(encoded_string) except binascii.Error: Modified: python/branches/release31-maint/Lib/email/test/test_email.py ============================================================================== --- python/branches/release31-maint/Lib/email/test/test_email.py (original) +++ python/branches/release31-maint/Lib/email/test/test_email.py Wed Aug 4 01:35:44 2010 @@ -1645,6 +1645,15 @@ (b'rg', None), (b'\xe5', 'iso-8859-1'), (b'sbord', None)]) + def test_rfc2047_B_bad_padding(self): + s = '=?iso-8859-1?B?%s?=' + data = [ # only test complete bytes + ('dm==', b'v'), ('dm=', b'v'), ('dm', b'v'), + ('dmk=', b'vi'), ('dmk', b'vi') + ] + for q, a in data: + dh = decode_header(s % q) + self.assertEqual(dh, [(a, 'iso-8859-1')]) # Test the MIMEMessage class @@ -3172,7 +3181,7 @@ def test_broken_base64_header(self): raises = self.assertRaises - s = 'Subject: =?EUC-KR?B?CSixpLDtKSC/7Liuvsax4iC6uLmwMcijIKHaILzSwd/H0SC8+LCjwLsgv7W/+Mj3IQ?=' + s = 'Subject: =?EUC-KR?B?CSixpLDtKSC/7Liuvsax4iC6uLmwMcijIKHaILzSwd/H0SC8+LCjwLsgv7W/+Mj3I ?=' raises(errors.HeaderParseError, decode_header, s) Modified: python/branches/release31-maint/Misc/ACKS ============================================================================== --- python/branches/release31-maint/Misc/ACKS (original) +++ python/branches/release31-maint/Misc/ACKS Wed Aug 4 01:35:44 2010 @@ -817,6 +817,7 @@ Gerry Wiener Frank Wierzbicki Bryce "Zooko" Wilcox-O'Hearn +Jason Williams John Williams Sue Williams Gerald S. Williams Modified: python/branches/release31-maint/Misc/NEWS ============================================================================== --- python/branches/release31-maint/Misc/NEWS (original) +++ python/branches/release31-maint/Misc/NEWS Wed Aug 4 01:35:44 2010 @@ -84,6 +84,9 @@ Library ------- +- Issue #3196: email header decoding is now forgiving if an RFC2047 + encoded word encoded in base64 is lacking padding. + - Issue #8447: Make distutils.sysconfig follow symlinks in the path to the interpreter executable. This fixes a failure of test_httpservers on OS X. From python-checkins at python.org Wed Aug 4 02:05:51 2010 From: python-checkins at python.org (r.david.murray) Date: Wed, 4 Aug 2010 02:05:51 +0200 (CEST) Subject: [Python-checkins] r83695 - in python/branches/release27-maint: Lib/email/header.py Lib/email/test/test_email.py Lib/email/test/test_email_renamed.py Misc/ACKS Misc/NEWS Message-ID: <20100804000551.1FF49EE987@mail.python.org> Author: r.david.murray Date: Wed Aug 4 02:05:50 2010 New Revision: 83695 Log: Merged revisions 83690 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83690 | r.david.murray | 2010-08-03 18:14:10 -0400 (Tue, 03 Aug 2010) | 10 lines #3196: if needed pad a short base64 encoded word before trying to decode. The RFCs encourage following Postel's law: be liberal in what you accept. So if someone forgot to pad the base64 encoded word payload to an even four bytes, we add the padding before handing it to base64mime.decode. Previously, missing padding resulted in a HeaderParseError. Patch by Jason Williams. ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Lib/email/header.py python/branches/release27-maint/Lib/email/test/test_email.py python/branches/release27-maint/Lib/email/test/test_email_renamed.py python/branches/release27-maint/Misc/ACKS python/branches/release27-maint/Misc/NEWS Modified: python/branches/release27-maint/Lib/email/header.py ============================================================================== --- python/branches/release27-maint/Lib/email/header.py (original) +++ python/branches/release27-maint/Lib/email/header.py Wed Aug 4 02:05:50 2010 @@ -92,6 +92,9 @@ if encoding == 'q': dec = email.quoprimime.header_decode(encoded) elif encoding == 'b': + paderr = len(encoded) % 4 # Postel's law: add missing padding + if paderr: + encoded += '==='[:4 - paderr] try: dec = email.base64mime.decode(encoded) except binascii.Error: Modified: python/branches/release27-maint/Lib/email/test/test_email.py ============================================================================== --- python/branches/release27-maint/Lib/email/test/test_email.py (original) +++ python/branches/release27-maint/Lib/email/test/test_email.py Wed Aug 4 02:05:50 2010 @@ -1611,6 +1611,15 @@ ('rg', None), ('\xe5', 'iso-8859-1'), ('sbord', None)]) + def test_rfc2047_B_bad_padding(self): + s = '=?iso-8859-1?B?%s?=' + data = [ # only test complete bytes + ('dm==', 'v'), ('dm=', 'v'), ('dm', 'v'), + ('dmk=', 'vi'), ('dmk', 'vi') + ] + for q, a in data: + dh = decode_header(s % q) + self.assertEqual(dh, [(a, 'iso-8859-1')]) # Test the MIMEMessage class @@ -3064,7 +3073,7 @@ def test_broken_base64_header(self): raises = self.assertRaises - s = 'Subject: =?EUC-KR?B?CSixpLDtKSC/7Liuvsax4iC6uLmwMcijIKHaILzSwd/H0SC8+LCjwLsgv7W/+Mj3IQ?=' + s = 'Subject: =?EUC-KR?B?CSixpLDtKSC/7Liuvsax4iC6uLmwMcijIKHaILzSwd/H0SC8+LCjwLsgv7W/+Mj3I ?=' raises(Errors.HeaderParseError, decode_header, s) Modified: python/branches/release27-maint/Lib/email/test/test_email_renamed.py ============================================================================== --- python/branches/release27-maint/Lib/email/test/test_email_renamed.py (original) +++ python/branches/release27-maint/Lib/email/test/test_email_renamed.py Wed Aug 4 02:05:50 2010 @@ -2958,7 +2958,7 @@ def test_broken_base64_header(self): raises = self.assertRaises - s = 'Subject: =?EUC-KR?B?CSixpLDtKSC/7Liuvsax4iC6uLmwMcijIKHaILzSwd/H0SC8+LCjwLsgv7W/+Mj3IQ?=' + s = 'Subject: =?EUC-KR?B?CSixpLDtKSC/7Liuvsax4iC6uLmwMcijIKHaILzSwd/H0SC8+LCjwLsgv7W/+Mj3I ?=' raises(errors.HeaderParseError, decode_header, s) Modified: python/branches/release27-maint/Misc/ACKS ============================================================================== --- python/branches/release27-maint/Misc/ACKS (original) +++ python/branches/release27-maint/Misc/ACKS Wed Aug 4 02:05:50 2010 @@ -831,6 +831,7 @@ Gerry Wiener Frank Wierzbicki Bryce "Zooko" Wilcox-O'Hearn +Jason Williams John Williams Sue Williams Gerald S. Williams Modified: python/branches/release27-maint/Misc/NEWS ============================================================================== --- python/branches/release27-maint/Misc/NEWS (original) +++ python/branches/release27-maint/Misc/NEWS Wed Aug 4 02:05:50 2010 @@ -24,6 +24,9 @@ Library ------- +- Issue #3196: email header decoding is now forgiving if an RFC2047 + encoded word encoded in base64 is lacking padding. + - Issue #9444: Argparse now uses the first element of prefix_chars as the option character for the added 'h/help' option if prefix_chars does not contain a '-', instead of raising an error. From python-checkins at python.org Wed Aug 4 02:18:50 2010 From: python-checkins at python.org (antoine.pitrou) Date: Wed, 4 Aug 2010 02:18:50 +0200 (CEST) Subject: [Python-checkins] r83696 - python/branches/py3k/Lib/test/test_threading_local.py Message-ID: <20100804001850.2525EEE9B4@mail.python.org> Author: antoine.pitrou Date: Wed Aug 4 02:18:49 2010 New Revision: 83696 Log: That test was never run (since thread has been renamed to _thread in 3.x) Modified: python/branches/py3k/Lib/test/test_threading_local.py Modified: python/branches/py3k/Lib/test/test_threading_local.py ============================================================================== --- python/branches/py3k/Lib/test/test_threading_local.py (original) +++ python/branches/py3k/Lib/test/test_threading_local.py Wed Aug 4 02:18:49 2010 @@ -137,20 +137,14 @@ suite.addTest(unittest.makeSuite(ThreadLocalTest)) suite.addTest(unittest.makeSuite(PyThreadingLocalTest)) - try: - from thread import _local - except ImportError: - pass - else: - import _threading_local - local_orig = _threading_local.local - def setUp(test): - _threading_local.local = _local - def tearDown(test): - _threading_local.local = local_orig - suite.addTest(DocTestSuite('_threading_local', - setUp=setUp, tearDown=tearDown) - ) + local_orig = _threading_local.local + def setUp(test): + _threading_local.local = _thread._local + def tearDown(test): + _threading_local.local = local_orig + suite.addTest(DocTestSuite('_threading_local', + setUp=setUp, tearDown=tearDown) + ) support.run_unittest(suite) From python-checkins at python.org Wed Aug 4 02:19:35 2010 From: python-checkins at python.org (antoine.pitrou) Date: Wed, 4 Aug 2010 02:19:35 +0200 (CEST) Subject: [Python-checkins] r83697 - in python/branches/release31-maint: Lib/test/test_threading_local.py Message-ID: <20100804001935.76B0AEEA01@mail.python.org> Author: antoine.pitrou Date: Wed Aug 4 02:19:35 2010 New Revision: 83697 Log: Merged revisions 83696 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83696 | antoine.pitrou | 2010-08-04 02:18:49 +0200 (mer., 04 ao?t 2010) | 3 lines That test was never run (since thread has been renamed to _thread in 3.x) ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Lib/test/test_threading_local.py Modified: python/branches/release31-maint/Lib/test/test_threading_local.py ============================================================================== --- python/branches/release31-maint/Lib/test/test_threading_local.py (original) +++ python/branches/release31-maint/Lib/test/test_threading_local.py Wed Aug 4 02:19:35 2010 @@ -126,20 +126,14 @@ suite.addTest(unittest.makeSuite(ThreadLocalTest)) suite.addTest(unittest.makeSuite(PyThreadingLocalTest)) - try: - from thread import _local - except ImportError: - pass - else: - import _threading_local - local_orig = _threading_local.local - def setUp(test): - _threading_local.local = _local - def tearDown(test): - _threading_local.local = local_orig - suite.addTest(DocTestSuite('_threading_local', - setUp=setUp, tearDown=tearDown) - ) + local_orig = _threading_local.local + def setUp(test): + _threading_local.local = _thread._local + def tearDown(test): + _threading_local.local = local_orig + suite.addTest(DocTestSuite('_threading_local', + setUp=setUp, tearDown=tearDown) + ) support.run_unittest(suite) From python-checkins at python.org Wed Aug 4 03:19:22 2010 From: python-checkins at python.org (richard.jones) Date: Wed, 4 Aug 2010 03:19:22 +0200 (CEST) Subject: [Python-checkins] r83698 - python/branches/py3k/Misc/NEWS Message-ID: <20100804011922.3182CEE9AE@mail.python.org> Author: richard.jones Date: Wed Aug 4 03:19:22 2010 New Revision: 83698 Log: note smtpd module changes in NEWS Modified: python/branches/py3k/Misc/NEWS Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Wed Aug 4 03:19:22 2010 @@ -37,6 +37,9 @@ Library ------- +- Issue #4184: Private attributes on smtpd.SMTPChannel made public and + deprecate the private attributes. Add tests for smtpd module. + - Issue #3196: email header decoding is now forgiving if an RFC2047 encoded word encoded in base64 is lacking padding. From python-checkins at python.org Wed Aug 4 03:20:14 2010 From: python-checkins at python.org (richard.jones) Date: Wed, 4 Aug 2010 03:20:14 +0200 (CEST) Subject: [Python-checkins] r83699 - python/branches/py3k/Lib/test/test_smtpd.py Message-ID: <20100804012014.F3FF8EEA31@mail.python.org> Author: richard.jones Date: Wed Aug 4 03:20:14 2010 New Revision: 83699 Log: improve smtpd module test coverage Modified: python/branches/py3k/Lib/test/test_smtpd.py Modified: python/branches/py3k/Lib/test/test_smtpd.py ============================================================================== --- python/branches/py3k/Lib/test/test_smtpd.py (original) +++ python/branches/py3k/Lib/test/test_smtpd.py Wed Aug 4 03:20:14 2010 @@ -7,8 +7,8 @@ class DummyServer(smtpd.SMTPServer): - def __init__(self, *args): - smtpd.SMTPServer.__init__(self, *args) + def __init__(self, localaddr, remoteaddr): + smtpd.SMTPServer.__init__(self, localaddr, remoteaddr) self.messages = [] def process_message(self, peer, mailfrom, rcpttos, data): @@ -16,13 +16,38 @@ if data == 'return status': return '250 Okish' + class DummyDispatcherBroken(Exception): pass + class BrokenDummyServer(DummyServer): def listen(self, num): raise DummyDispatcherBroken() + +class SMTPDServerTest(TestCase): + def setUp(self): + smtpd.socket = asyncore.socket = mock_socket + + def test_process_message_unimplemented(self): + server = smtpd.SMTPServer('a', 'b') + conn, addr = server.accept() + channel = smtpd.SMTPChannel(server, conn, addr) + + def write_line(line): + channel.socket.queue_recv(line) + channel.handle_read() + + write_line(b'MAIL From:eggs at example') + write_line(b'RCPT To:spam at example') + write_line(b'DATA') + self.assertRaises(NotImplementedError, write_line, b'spam\r\n.\r\n') + + def tearDown(self): + asyncore.socket = smtpd.socket = socket + + class SMTPDChannelTest(TestCase): def setUp(self): smtpd.socket = asyncore.socket = mock_socket @@ -142,8 +167,8 @@ b'354 End data with .\r\n') self.write_line(b'data\r\nmore\r\n.') self.assertEqual(self.channel.socket.last, b'250 Ok\r\n') - self.assertEqual(self.server.messages[-1], - ('peer', 'eggs at example', ['spam at example'], 'data\nmore')) + self.assertEqual(self.server.messages, + [('peer', 'eggs at example', ['spam at example'], 'data\nmore')]) def test_DATA_syntax(self): self.write_line(b'MAIL From:eggs at example') @@ -151,6 +176,13 @@ self.write_line(b'DATA spam') self.assertEqual(self.channel.socket.last, b'501 Syntax: DATA\r\n') + def test_data_transparency_section_4_5_2(self): + self.write_line(b'MAIL From:eggs at example') + self.write_line(b'RCPT To:spam at example') + self.write_line(b'DATA') + self.write_line(b'..\r\n.\r\n') + self.assertEqual(self.channel.received_data, '.') + def test_multiple_RCPT(self): self.write_line(b'MAIL From:eggs at example') self.write_line(b'RCPT To:spam at example') @@ -161,6 +193,7 @@ ('peer', 'eggs at example', ['spam at example','ham at example'], 'data')) def test_manual_status(self): + # checks that the Channel is able to return a custom status message self.write_line(b'MAIL From:eggs at example') self.write_line(b'RCPT To:spam at example') self.write_line(b'DATA') @@ -183,9 +216,54 @@ self.write_line(b'RSET hi') self.assertEqual(self.channel.socket.last, b'501 Syntax: RSET\r\n') + def test_attribute_deprecations(self): + with support.check_warnings(('', PendingDeprecationWarning)): + spam = self.channel._SMTPChannel__server + with support.check_warnings(('', PendingDeprecationWarning)): + self.channel._SMTPChannel__server = 'spam' + with support.check_warnings(('', PendingDeprecationWarning)): + spam = self.channel._SMTPChannel__line + with support.check_warnings(('', PendingDeprecationWarning)): + self.channel._SMTPChannel__line = 'spam' + with support.check_warnings(('', PendingDeprecationWarning)): + spam = self.channel._SMTPChannel__state + with support.check_warnings(('', PendingDeprecationWarning)): + self.channel._SMTPChannel__state = 'spam' + with support.check_warnings(('', PendingDeprecationWarning)): + spam = self.channel._SMTPChannel__greeting + with support.check_warnings(('', PendingDeprecationWarning)): + self.channel._SMTPChannel__greeting = 'spam' + with support.check_warnings(('', PendingDeprecationWarning)): + spam = self.channel._SMTPChannel__mailfrom + with support.check_warnings(('', PendingDeprecationWarning)): + self.channel._SMTPChannel__mailfrom = 'spam' + with support.check_warnings(('', PendingDeprecationWarning)): + spam = self.channel._SMTPChannel__rcpttos + with support.check_warnings(('', PendingDeprecationWarning)): + self.channel._SMTPChannel__rcpttos = 'spam' + with support.check_warnings(('', PendingDeprecationWarning)): + spam = self.channel._SMTPChannel__data + with support.check_warnings(('', PendingDeprecationWarning)): + self.channel._SMTPChannel__data = 'spam' + with support.check_warnings(('', PendingDeprecationWarning)): + spam = self.channel._SMTPChannel__fqdn + with support.check_warnings(('', PendingDeprecationWarning)): + self.channel._SMTPChannel__fqdn = 'spam' + with support.check_warnings(('', PendingDeprecationWarning)): + spam = self.channel._SMTPChannel__peer + with support.check_warnings(('', PendingDeprecationWarning)): + self.channel._SMTPChannel__peer = 'spam' + with support.check_warnings(('', PendingDeprecationWarning)): + spam = self.channel._SMTPChannel__conn + with support.check_warnings(('', PendingDeprecationWarning)): + self.channel._SMTPChannel__conn = 'spam' + with support.check_warnings(('', PendingDeprecationWarning)): + spam = self.channel._SMTPChannel__addr + with support.check_warnings(('', PendingDeprecationWarning)): + self.channel._SMTPChannel__addr = 'spam' def test_main(): - support.run_unittest(SMTPDChannelTest) + support.run_unittest(SMTPDServerTest, SMTPDChannelTest) if __name__ == "__main__": test_main() From solipsis at pitrou.net Wed Aug 4 05:03:16 2010 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Wed, 04 Aug 2010 05:03:16 +0200 Subject: [Python-checkins] Daily py3k reference leaks (r83699): sum=0 Message-ID: py3k results for svn r83699 (hg cset eede126ee89a) -------------------------------------------------- Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/py3k/refleaks/reflogpd8zHs', '-x'] From python-checkins at python.org Wed Aug 4 06:45:31 2010 From: python-checkins at python.org (senthil.kumaran) Date: Wed, 4 Aug 2010 06:45:31 +0200 (CEST) Subject: [Python-checkins] r83700 - in python/branches/release27-maint: Doc/library/urlparse.rst Lib/test/test_urlparse.py Lib/urlparse.py Message-ID: <20100804044531.868C3EE98F@mail.python.org> Author: senthil.kumaran Date: Wed Aug 4 06:45:31 2010 New Revision: 83700 Log: Fix Issue754016 - urlparse goes wrong with IP:port without scheme Modified: python/branches/release27-maint/Doc/library/urlparse.rst python/branches/release27-maint/Lib/test/test_urlparse.py python/branches/release27-maint/Lib/urlparse.py Modified: python/branches/release27-maint/Doc/library/urlparse.rst ============================================================================== --- python/branches/release27-maint/Doc/library/urlparse.rst (original) +++ python/branches/release27-maint/Doc/library/urlparse.rst Wed Aug 4 06:45:31 2010 @@ -58,6 +58,24 @@ >>> o.geturl() 'http://www.cwi.nl:80/%7Eguido/Python.html' + + If the scheme value is not specified, urlparse following the syntax + specifications from RFC 1808, expects the netloc value to start with '//', + Otherwise, it is not possible to distinguish between net_loc and path + component and would classify the indistinguishable component as path as in + a relative url. + + >>> from urlparse import urlparse + >>> urlparse('//www.cwi.nl:80/%7Eguido/Python.html') + ParseResult(scheme='', netloc='www.cwi.nl:80', path='/%7Eguido/Python.html', + params='', query='', fragment='') + >>> urlparse('www.cwi.nl:80/%7Eguido/Python.html') + ParseResult(scheme='', netloc='', path='www.cwi.nl:80/%7Eguido/Python.html', + params='', query='', fragment='') + >>> urlparse('help/Python.html') + ParseResult(scheme='', netloc='', path='help/Python.html', params='', + query='', fragment='') + If the *scheme* argument is specified, it gives the default addressing scheme, to be used only if the URL does not specify one. The default value for this argument is the empty string. Modified: python/branches/release27-maint/Lib/test/test_urlparse.py ============================================================================== --- python/branches/release27-maint/Lib/test/test_urlparse.py (original) +++ python/branches/release27-maint/Lib/test/test_urlparse.py Wed Aug 4 06:45:31 2010 @@ -478,6 +478,26 @@ self.assertEqual(urlparse.urlparse("x-newscheme://foo.com/stuff"), ('x-newscheme','foo.com','/stuff','','','')) + def test_withoutscheme(self): + # Test urlparse without scheme + # Issue 754016: urlparse goes wrong with IP:port without scheme + # RFC 1808 specifies that netloc should start with //, urlparse expects + # the same, otherwise it classifies the portion of url as path. + self.assertEqual(urlparse.urlparse("path"), + ('','','path','','','')) + self.assertEqual(urlparse.urlparse("//www.python.org:80"), + ('','www.python.org:80','','','','')) + self.assertEqual(urlparse.urlparse("http://www.python.org:80"), + ('http','www.python.org:80','','','','')) + + def test_portseparator(self): + # Issue 754016 makes changes for port separator ':' from scheme separator + self.assertEqual(urlparse.urlparse("path:80"), + ('','','path:80','','','')) + self.assertEqual(urlparse.urlparse("http:"),('http','','','','','')) + self.assertEqual(urlparse.urlparse("https:"),('https','','','','','')) + self.assertEqual(urlparse.urlparse("http://www.python.org:80"), + ('http','www.python.org:80','','','','')) def test_main(): Modified: python/branches/release27-maint/Lib/urlparse.py ============================================================================== --- python/branches/release27-maint/Lib/urlparse.py (original) +++ python/branches/release27-maint/Lib/urlparse.py Wed Aug 4 06:45:31 2010 @@ -187,11 +187,12 @@ v = SplitResult(scheme, netloc, url, query, fragment) _parse_cache[key] = v return v - for c in url[:i]: - if c not in scheme_chars: - break - else: - scheme, url = url[:i].lower(), url[i+1:] + if url.endswith(':') or not url[i+1].isdigit(): + for c in url[:i]: + if c not in scheme_chars: + break + else: + scheme, url = url[:i].lower(), url[i+1:] if url[:2] == '//': netloc, url = _splitnetloc(url, 2) From python-checkins at python.org Wed Aug 4 06:50:44 2010 From: python-checkins at python.org (senthil.kumaran) Date: Wed, 4 Aug 2010 06:50:44 +0200 (CEST) Subject: [Python-checkins] r83701 - in python/branches/py3k: Doc/library/urllib.parse.rst Lib/test/test_urlparse.py Lib/urllib/parse.py Message-ID: <20100804045044.DBA91EE9A0@mail.python.org> Author: senthil.kumaran Date: Wed Aug 4 06:50:44 2010 New Revision: 83701 Log: Fix Issue754016 - urlparse goes wrong with IP:port without scheme Modified: python/branches/py3k/Doc/library/urllib.parse.rst python/branches/py3k/Lib/test/test_urlparse.py python/branches/py3k/Lib/urllib/parse.py Modified: python/branches/py3k/Doc/library/urllib.parse.rst ============================================================================== --- python/branches/py3k/Doc/library/urllib.parse.rst (original) +++ python/branches/py3k/Doc/library/urllib.parse.rst Wed Aug 4 06:50:44 2010 @@ -48,6 +48,23 @@ >>> o.geturl() 'http://www.cwi.nl:80/%7Eguido/Python.html' + If the scheme value is not specified, urlparse following the syntax + specifications from RFC 1808, expects the netloc value to start with '//', + Otherwise, it is not possible to distinguish between net_loc and path + component and would classify the indistinguishable component as path as in + a relative url. + + >>> from urlparse import urlparse + >>> urlparse('//www.cwi.nl:80/%7Eguido/Python.html') + ParseResult(scheme='', netloc='www.cwi.nl:80', path='/%7Eguido/Python.html', + params='', query='', fragment='') + >>> urlparse('www.cwi.nl:80/%7Eguido/Python.html') + ParseResult(scheme='', netloc='', path='www.cwi.nl:80/%7Eguido/Python.html', + params='', query='', fragment='') + >>> urlparse('help/Python.html') + ParseResult(scheme='', netloc='', path='help/Python.html', params='', + query='', fragment='') + If the *scheme* argument is specified, it gives the default addressing scheme, to be used only if the URL does not specify one. The default value for this argument is the empty string. Modified: python/branches/py3k/Lib/test/test_urlparse.py ============================================================================== --- python/branches/py3k/Lib/test/test_urlparse.py (original) +++ python/branches/py3k/Lib/test/test_urlparse.py Wed Aug 4 06:50:44 2010 @@ -461,6 +461,27 @@ self.assertEqual(urllib.parse.urlparse("http://example.com?blahblah=/foo"), ('http', 'example.com', '', '', 'blahblah=/foo', '')) + def test_withoutscheme(self): + # Test urlparse without scheme + # Issue 754016: urlparse goes wrong with IP:port without scheme + # RFC 1808 specifies that netloc should start with //, urlparse expects + # the same, otherwise it classifies the portion of url as path. + self.assertEqual(urllib.parse.urlparse("path"), + ('','','path','','','')) + self.assertEqual(urllib.parse.urlparse("//www.python.org:80"), + ('','www.python.org:80','','','','')) + self.assertEqual(urllib.parse.urlparse("http://www.python.org:80"), + ('http','www.python.org:80','','','','')) + + def test_portseparator(self): + # Issue 754016 makes changes for port separator ':' from scheme separator + self.assertEqual(urllib.parse.urlparse("path:80"), + ('','','path:80','','','')) + self.assertEqual(urllib.parse.urlparse("http:"),('http','','','','','')) + self.assertEqual(urllib.parse.urlparse("https:"),('https','','','','','')) + self.assertEqual(urllib.parse.urlparse("http://www.python.org:80"), + ('http','www.python.org:80','','','','')) + def test_usingsys(self): # Issue 3314: sys module is used in the error self.assertRaises(TypeError, urllib.parse.urlencode, "foo") Modified: python/branches/py3k/Lib/urllib/parse.py ============================================================================== --- python/branches/py3k/Lib/urllib/parse.py (original) +++ python/branches/py3k/Lib/urllib/parse.py Wed Aug 4 06:50:44 2010 @@ -192,11 +192,12 @@ v = SplitResult(scheme, netloc, url, query, fragment) _parse_cache[key] = v return v - for c in url[:i]: - if c not in scheme_chars: - break - else: - scheme, url = url[:i].lower(), url[i+1:] + if url.endswith(':') or not url[i+1].isdigit(): + for c in url[:i]: + if c not in scheme_chars: + break + else: + scheme, url = url[:i].lower(), url[i+1:] if url[:2] == '//': netloc, url = _splitnetloc(url, 2) if (('[' in netloc and ']' not in netloc) or From python-checkins at python.org Wed Aug 4 06:53:07 2010 From: python-checkins at python.org (senthil.kumaran) Date: Wed, 4 Aug 2010 06:53:07 +0200 (CEST) Subject: [Python-checkins] r83702 - in python/branches/release31-maint: Doc/library/urllib.parse.rst Lib/test/test_urlparse.py Lib/urllib/parse.py Message-ID: <20100804045307.A2C7AEE9FB@mail.python.org> Author: senthil.kumaran Date: Wed Aug 4 06:53:07 2010 New Revision: 83702 Log: Merged revisions 83701 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83701 | senthil.kumaran | 2010-08-04 10:20:44 +0530 (Wed, 04 Aug 2010) | 3 lines Fix Issue754016 - urlparse goes wrong with IP:port without scheme ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Doc/library/urllib.parse.rst python/branches/release31-maint/Lib/test/test_urlparse.py python/branches/release31-maint/Lib/urllib/parse.py Modified: python/branches/release31-maint/Doc/library/urllib.parse.rst ============================================================================== --- python/branches/release31-maint/Doc/library/urllib.parse.rst (original) +++ python/branches/release31-maint/Doc/library/urllib.parse.rst Wed Aug 4 06:53:07 2010 @@ -48,6 +48,23 @@ >>> o.geturl() 'http://www.cwi.nl:80/%7Eguido/Python.html' + If the scheme value is not specified, urlparse following the syntax + specifications from RFC 1808, expects the netloc value to start with '//', + Otherwise, it is not possible to distinguish between net_loc and path + component and would classify the indistinguishable component as path as in + a relative url. + + >>> from urlparse import urlparse + >>> urlparse('//www.cwi.nl:80/%7Eguido/Python.html') + ParseResult(scheme='', netloc='www.cwi.nl:80', path='/%7Eguido/Python.html', + params='', query='', fragment='') + >>> urlparse('www.cwi.nl:80/%7Eguido/Python.html') + ParseResult(scheme='', netloc='', path='www.cwi.nl:80/%7Eguido/Python.html', + params='', query='', fragment='') + >>> urlparse('help/Python.html') + ParseResult(scheme='', netloc='', path='help/Python.html', params='', + query='', fragment='') + If the *scheme* argument is specified, it gives the default addressing scheme, to be used only if the URL does not specify one. The default value for this argument is the empty string. Modified: python/branches/release31-maint/Lib/test/test_urlparse.py ============================================================================== --- python/branches/release31-maint/Lib/test/test_urlparse.py (original) +++ python/branches/release31-maint/Lib/test/test_urlparse.py Wed Aug 4 06:53:07 2010 @@ -424,6 +424,27 @@ self.assertEqual(urllib.parse.urlparse("http://example.com?blahblah=/foo"), ('http', 'example.com', '', '', 'blahblah=/foo', '')) + def test_withoutscheme(self): + # Test urlparse without scheme + # Issue 754016: urlparse goes wrong with IP:port without scheme + # RFC 1808 specifies that netloc should start with //, urlparse expects + # the same, otherwise it classifies the portion of url as path. + self.assertEqual(urllib.parse.urlparse("path"), + ('','','path','','','')) + self.assertEqual(urllib.parse.urlparse("//www.python.org:80"), + ('','www.python.org:80','','','','')) + self.assertEqual(urllib.parse.urlparse("http://www.python.org:80"), + ('http','www.python.org:80','','','','')) + + def test_portseparator(self): + # Issue 754016 makes changes for port separator ':' from scheme separator + self.assertEqual(urllib.parse.urlparse("path:80"), + ('','','path:80','','','')) + self.assertEqual(urllib.parse.urlparse("http:"),('http','','','','','')) + self.assertEqual(urllib.parse.urlparse("https:"),('https','','','','','')) + self.assertEqual(urllib.parse.urlparse("http://www.python.org:80"), + ('http','www.python.org:80','','','','')) + def test_usingsys(self): # Issue 3314: sys module is used in the error self.assertRaises(TypeError, urllib.parse.urlencode, "foo") Modified: python/branches/release31-maint/Lib/urllib/parse.py ============================================================================== --- python/branches/release31-maint/Lib/urllib/parse.py (original) +++ python/branches/release31-maint/Lib/urllib/parse.py Wed Aug 4 06:53:07 2010 @@ -183,11 +183,12 @@ v = SplitResult(scheme, netloc, url, query, fragment) _parse_cache[key] = v return v - for c in url[:i]: - if c not in scheme_chars: - break - else: - scheme, url = url[:i].lower(), url[i+1:] + if url.endswith(':') or not url[i+1].isdigit(): + for c in url[:i]: + if c not in scheme_chars: + break + else: + scheme, url = url[:i].lower(), url[i+1:] if url[:2] == '//': netloc, url = _splitnetloc(url, 2) if allow_fragments and scheme in uses_fragment and '#' in url: From ezio.melotti at gmail.com Wed Aug 4 07:51:23 2010 From: ezio.melotti at gmail.com (Ezio Melotti) Date: Wed, 04 Aug 2010 08:51:23 +0300 Subject: [Python-checkins] r83398 - in python/branches/py3k: Lib/http/cookies.py Lib/test/test_http_cookies.py Misc/NEWS In-Reply-To: <20100801090634.ABABAEE984@mail.python.org> References: <20100801090634.ABABAEE984@mail.python.org> Message-ID: <4C58FFDB.4010805@gmail.com> Hi, On 01/08/2010 12.06, georg.brandl wrote: > Author: georg.brandl > Date: Sun Aug 1 11:06:34 2010 > New Revision: 83398 > > Log: > #8826: the "expires" attribute value is a date string with spaces, but apparently not all user-agents put it in quotes. Handle that as a special case. > > Modified: > python/branches/py3k/Lib/http/cookies.py > python/branches/py3k/Lib/test/test_http_cookies.py > python/branches/py3k/Misc/NEWS > > Modified: python/branches/py3k/Lib/http/cookies.py > ============================================================================== > --- python/branches/py3k/Lib/http/cookies.py (original) > +++ python/branches/py3k/Lib/http/cookies.py Sun Aug 1 11:06:34 2010 > @@ -434,6 +434,8 @@ > (?P # Start of group 'val' > "(?:[^\\"]|\\.)*" # Any doublequoted string > | # or > + \w{3},\s[\w\d-]{9,11}\s[\d:]{8}\sGM Isn't the \d in [\w\d-] redundant? > T # Special case for "expires" attr > + | # or > """ + _LegalCharsPatt + r"""* # Any word or empty string > ) # End of group 'val' > \s*;? # Probably ending in a semi-colon > > Modified: python/branches/py3k/Lib/test/test_http_cookies.py > ============================================================================== > --- python/branches/py3k/Lib/test/test_http_cookies.py (original) > +++ python/branches/py3k/Lib/test/test_http_cookies.py Sun Aug 1 11:06:34 2010 > @@ -76,6 +76,16 @@ > # can't test exact output, it always depends on current date/time > self.assertTrue(C.output().endswith('GMT')) > > + # loading 'expires' > + C = cookies.SimpleCookie() > + C.load('Customer="W"; expires=Wed, 01-Jan-2010 00:00:00 GMT') > + self.assertEqual(C['Customer']['expires'], > + 'Wed, 01-Jan-2010 00:00:00 GMT') > + C = cookies.SimpleCookie() > + C.load('Customer="W"; expires=Wed, 01-Jan-98 00:00:00 GMT') > + self.assertEqual(C['Customer']['expires'], > + 'Wed, 01-Jan-98 00:00:00 GMT') > + > # 'max-age' > C = cookies.SimpleCookie('Customer="WILE_E_COYOTE"') > C['Customer']['max-age'] = 10 > > Modified: python/branches/py3k/Misc/NEWS > ============================================================================== > --- python/branches/py3k/Misc/NEWS (original) > +++ python/branches/py3k/Misc/NEWS Sun Aug 1 11:06:34 2010 > @@ -15,6 +15,8 @@ > Library > ------- > > +- Issue #8826: Properly load old-style "expires" attribute in http.cookies. > + > - Issue #1690103: Fix initial namespace for code run with trace.main(). > > - Issue #7395: Fix tracebacks in pstats interactive browser. > _______________________________________________ > Python-checkins mailing list > Python-checkins at python.org > http://mail.python.org/mailman/listinfo/python-checkins > Best Regards, Ezio Melotti From python-checkins at python.org Wed Aug 4 10:35:25 2010 From: python-checkins at python.org (giampaolo.rodola) Date: Wed, 4 Aug 2010 10:35:25 +0200 (CEST) Subject: [Python-checkins] r83703 - in python/branches/release27-maint: Lib/asyncore.py Misc/ACKS Misc/NEWS Message-ID: <20100804083525.3D8F4EEA73@mail.python.org> Author: giampaolo.rodola Date: Wed Aug 4 10:35:25 2010 New Revision: 83703 Log: fix issue #2944: asyncore doesn't handle connection refused correctly (patch by Alexander Shigin) Modified: python/branches/release27-maint/Lib/asyncore.py python/branches/release27-maint/Misc/ACKS python/branches/release27-maint/Misc/NEWS Modified: python/branches/release27-maint/Lib/asyncore.py ============================================================================== --- python/branches/release27-maint/Lib/asyncore.py (original) +++ python/branches/release27-maint/Lib/asyncore.py Wed Aug 4 10:35:25 2010 @@ -435,8 +435,11 @@ self.handle_read() def handle_connect_event(self): - self.connected = True + err = self.socket.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR) + if err != 0: + raise socket.error(err, _strerror(err)) self.handle_connect() + self.connected = True def handle_write_event(self): if self.accepting: Modified: python/branches/release27-maint/Misc/ACKS ============================================================================== --- python/branches/release27-maint/Misc/ACKS (original) +++ python/branches/release27-maint/Misc/ACKS Wed Aug 4 10:35:25 2010 @@ -871,3 +871,4 @@ Tarek Ziad? Peter ?strand Andrej Krpic +Alexander Shigin Modified: python/branches/release27-maint/Misc/NEWS ============================================================================== --- python/branches/release27-maint/Misc/NEWS (original) +++ python/branches/release27-maint/Misc/NEWS Wed Aug 4 10:35:25 2010 @@ -24,6 +24,8 @@ Library ------- +- Issue #2944: asyncore doesn't handle connection refused correctly. + - Issue #3196: email header decoding is now forgiving if an RFC2047 encoded word encoded in base64 is lacking padding. From python-checkins at python.org Wed Aug 4 10:58:38 2010 From: python-checkins at python.org (giampaolo.rodola) Date: Wed, 4 Aug 2010 10:58:38 +0200 (CEST) Subject: [Python-checkins] r83704 - in python/branches/release26-maint: Lib/asyncore.py Misc/ACKS Misc/NEWS Message-ID: <20100804085838.3D95EEE994@mail.python.org> Author: giampaolo.rodola Date: Wed Aug 4 10:58:38 2010 New Revision: 83704 Log: Merged revisions 83703 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/release27-maint ........ r83703 | giampaolo.rodola | 2010-08-04 10:35:25 +0200 (mer, 04 ago 2010) | 1 line fix issue #2944: asyncore doesn't handle connection refused correctly (patch by Alexander Shigin) ........ Modified: python/branches/release26-maint/ (props changed) python/branches/release26-maint/Lib/asyncore.py python/branches/release26-maint/Misc/ACKS python/branches/release26-maint/Misc/NEWS Modified: python/branches/release26-maint/Lib/asyncore.py ============================================================================== --- python/branches/release26-maint/Lib/asyncore.py (original) +++ python/branches/release26-maint/Lib/asyncore.py Wed Aug 4 10:58:38 2010 @@ -422,8 +422,11 @@ self.handle_read() def handle_connect_event(self): - self.connected = True + err = self.socket.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR) + if err != 0: + raise socket.error(err, _strerror(err)) self.handle_connect() + self.connected = True def handle_write_event(self): if self.accepting: Modified: python/branches/release26-maint/Misc/ACKS ============================================================================== --- python/branches/release26-maint/Misc/ACKS (original) +++ python/branches/release26-maint/Misc/ACKS Wed Aug 4 10:58:38 2010 @@ -817,3 +817,4 @@ Peter ?strand Jesse Noller Fredrik H??rd +Alexander Shigin Modified: python/branches/release26-maint/Misc/NEWS ============================================================================== --- python/branches/release26-maint/Misc/NEWS (original) +++ python/branches/release26-maint/Misc/NEWS Wed Aug 4 10:58:38 2010 @@ -89,6 +89,8 @@ Library ------- +- Issue #2944: asyncore doesn't handle connection refused correctly. + - Issue #8447: Make distutils.sysconfig follow symlinks in the path to the interpreter executable. This fixes a failure of test_httpservers on OS X. From python-checkins at python.org Wed Aug 4 11:02:28 2010 From: python-checkins at python.org (giampaolo.rodola) Date: Wed, 4 Aug 2010 11:02:28 +0200 (CEST) Subject: [Python-checkins] r83705 - in python/branches/py3k: Lib/asyncore.py Misc/ACKS Misc/NEWS Message-ID: <20100804090228.11E8BEE994@mail.python.org> Author: giampaolo.rodola Date: Wed Aug 4 11:02:27 2010 New Revision: 83705 Log: fix issue #2944: asyncore doesn't handle connection refused correctly (patch by Alexander Shigin). Merged from 2.7 branch. Modified: python/branches/py3k/Lib/asyncore.py python/branches/py3k/Misc/ACKS python/branches/py3k/Misc/NEWS Modified: python/branches/py3k/Lib/asyncore.py ============================================================================== --- python/branches/py3k/Lib/asyncore.py (original) +++ python/branches/py3k/Lib/asyncore.py Wed Aug 4 11:02:27 2010 @@ -435,8 +435,11 @@ self.handle_read() def handle_connect_event(self): - self.connected = True + err = self.socket.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR) + if err != 0: + raise socket.error(err, _strerror(err)) self.handle_connect() + self.connected = True def handle_write_event(self): if self.accepting: Modified: python/branches/py3k/Misc/ACKS ============================================================================== --- python/branches/py3k/Misc/ACKS (original) +++ python/branches/py3k/Misc/ACKS Wed Aug 4 11:02:27 2010 @@ -895,3 +895,4 @@ Uwe Zessin Tarek Ziad? Peter ?strand +Alexander Shigin Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Wed Aug 4 11:02:27 2010 @@ -37,6 +37,8 @@ Library ------- +- Issue #2944: asyncore doesn't handle connection refused correctly. + - Issue #4184: Private attributes on smtpd.SMTPChannel made public and deprecate the private attributes. Add tests for smtpd module. From python-checkins at python.org Wed Aug 4 11:04:53 2010 From: python-checkins at python.org (giampaolo.rodola) Date: Wed, 4 Aug 2010 11:04:53 +0200 (CEST) Subject: [Python-checkins] r83706 - in python/branches/release31-maint: Lib/asyncore.py Misc/ACKS Misc/NEWS Message-ID: <20100804090453.A321BEE994@mail.python.org> Author: giampaolo.rodola Date: Wed Aug 4 11:04:53 2010 New Revision: 83706 Log: Merged revisions 83705 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83705 | giampaolo.rodola | 2010-08-04 11:02:27 +0200 (mer, 04 ago 2010) | 1 line fix issue #2944: asyncore doesn't handle connection refused correctly (patch by Alexander Shigin). Merged from 2.7 branch. ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Lib/asyncore.py python/branches/release31-maint/Misc/ACKS python/branches/release31-maint/Misc/NEWS Modified: python/branches/release31-maint/Lib/asyncore.py ============================================================================== --- python/branches/release31-maint/Lib/asyncore.py (original) +++ python/branches/release31-maint/Lib/asyncore.py Wed Aug 4 11:04:53 2010 @@ -426,8 +426,11 @@ self.handle_read() def handle_connect_event(self): - self.connected = True + err = self.socket.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR) + if err != 0: + raise socket.error(err, _strerror(err)) self.handle_connect() + self.connected = True def handle_write_event(self): if self.accepting: Modified: python/branches/release31-maint/Misc/ACKS ============================================================================== --- python/branches/release31-maint/Misc/ACKS (original) +++ python/branches/release31-maint/Misc/ACKS Wed Aug 4 11:04:53 2010 @@ -856,3 +856,4 @@ Tarek Ziad? Peter ?strand Fredrik H??rd +Alexander Shigin Modified: python/branches/release31-maint/Misc/NEWS ============================================================================== --- python/branches/release31-maint/Misc/NEWS (original) +++ python/branches/release31-maint/Misc/NEWS Wed Aug 4 11:04:53 2010 @@ -84,6 +84,8 @@ Library ------- +- Issue #2944: asyncore doesn't handle connection refused correctly. + - Issue #3196: email header decoding is now forgiving if an RFC2047 encoded word encoded in base64 is lacking padding. From python-checkins at python.org Wed Aug 4 11:28:05 2010 From: python-checkins at python.org (giampaolo.rodola) Date: Wed, 4 Aug 2010 11:28:05 +0200 (CEST) Subject: [Python-checkins] r83707 - in python/branches/py3k: Lib/test/test_sched.py Lib/test/test_sundry.py Misc/NEWS Message-ID: <20100804092805.B41B0EEA6F@mail.python.org> Author: giampaolo.rodola Date: Wed Aug 4 11:28:05 2010 New Revision: 83707 Log: issue #8687: provides a test suite for sched.py module Added: python/branches/py3k/Lib/test/test_sched.py Modified: python/branches/py3k/Lib/test/test_sundry.py python/branches/py3k/Misc/NEWS Added: python/branches/py3k/Lib/test/test_sched.py ============================================================================== --- (empty file) +++ python/branches/py3k/Lib/test/test_sched.py Wed Aug 4 11:28:05 2010 @@ -0,0 +1,80 @@ +#!/usr/bin/env python + +import sched +import time +import unittest +from test import support + + +class TestCase(unittest.TestCase): + + def test_enter(self): + l = [] + fun = lambda x: l.append(x) + scheduler = sched.scheduler(time.time, time.sleep) + for x in [0.05, 0.04, 0.03, 0.02, 0.01]: + z = scheduler.enter(x, 1, fun, (x,)) + scheduler.run() + self.assertEqual(l, [0.01, 0.02, 0.03, 0.04, 0.05]) + + def test_enterabs(self): + l = [] + fun = lambda x: l.append(x) + scheduler = sched.scheduler(time.time, time.sleep) + for x in [0.05, 0.04, 0.03, 0.02, 0.01]: + z = scheduler.enterabs(x, 1, fun, (x,)) + scheduler.run() + self.assertEqual(l, [0.01, 0.02, 0.03, 0.04, 0.05]) + + def test_priority(self): + l = [] + fun = lambda x: l.append(x) + scheduler = sched.scheduler(time.time, time.sleep) + for priority in [1, 2, 3, 4, 5]: + z = scheduler.enter(0.01, priority, fun, (priority,)) + scheduler.run() + self.assertEqual(l, [1, 2, 3, 4, 5]) + + def test_cancel(self): + l = [] + fun = lambda x: l.append(x) + scheduler = sched.scheduler(time.time, time.sleep) + event1 = scheduler.enter(0.01, 1, fun, (0.01,)) + event2 = scheduler.enter(0.02, 1, fun, (0.02,)) + event3 = scheduler.enter(0.03, 1, fun, (0.03,)) + event4 = scheduler.enter(0.04, 1, fun, (0.04,)) + event5 = scheduler.enter(0.05, 1, fun, (0.05,)) + scheduler.cancel(event1) + scheduler.cancel(event5) + scheduler.run() + self.assertEqual(l, [0.02, 0.03, 0.04]) + + def test_empty(self): + l = [] + fun = lambda x: l.append(x) + scheduler = sched.scheduler(time.time, time.sleep) + self.assertTrue(scheduler.empty()) + for x in [0.05, 0.04, 0.03, 0.02, 0.01]: + z = scheduler.enterabs(x, 1, fun, (x,)) + self.assertFalse(scheduler.empty()) + scheduler.run() + self.assertTrue(scheduler.empty()) + + def test_queue(self): + l = [] + events = [] + fun = lambda x: l.append(x) + scheduler = sched.scheduler(time.time, time.sleep) + self.assertEqual(scheduler._queue, []) + for x in [0.05, 0.04, 0.03, 0.02, 0.01]: + events.append(scheduler.enterabs(x, 1, fun, (x,))) + self.assertEqual(scheduler._queue.sort(), events.sort()) + scheduler.run() + self.assertEqual(scheduler._queue, []) + + +def test_main(): + support.run_unittest(TestCase) + +if __name__ == "__main__": + test_main() Modified: python/branches/py3k/Lib/test/test_sundry.py ============================================================================== --- python/branches/py3k/Lib/test/test_sundry.py (original) +++ python/branches/py3k/Lib/test/test_sundry.py Wed Aug 4 11:28:05 2010 @@ -57,7 +57,6 @@ import pstats import py_compile import rlcompleter - import sched import sndhdr import symbol import tabnanny Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Wed Aug 4 11:28:05 2010 @@ -105,6 +105,11 @@ - Issue #8867: Fix ``Tools/scripts/serve.py`` to work with files containing non-ASCII content. +Tests +----- + +- Issue #8687: provide a test suite for sched.py module. + What's New in Python 3.2 Alpha 1? ================================= From python-checkins at python.org Wed Aug 4 12:12:00 2010 From: python-checkins at python.org (giampaolo.rodola) Date: Wed, 4 Aug 2010 12:12:00 +0200 (CEST) Subject: [Python-checkins] r83708 - in python/branches/py3k: Lib/ftplib.py Lib/test/test_ftplib.py Misc/ACKS Misc/NEWS Message-ID: <20100804101200.56150ECFB@mail.python.org> Author: giampaolo.rodola Date: Wed Aug 4 12:12:00 2010 New Revision: 83708 Log: fix issue #6822: ftplib's storline method doesn't work with text files Modified: python/branches/py3k/Lib/ftplib.py python/branches/py3k/Lib/test/test_ftplib.py python/branches/py3k/Misc/ACKS python/branches/py3k/Misc/NEWS Modified: python/branches/py3k/Lib/ftplib.py ============================================================================== --- python/branches/py3k/Lib/ftplib.py (original) +++ python/branches/py3k/Lib/ftplib.py Wed Aug 4 12:12:00 2010 @@ -493,9 +493,15 @@ while 1: buf = fp.readline() if not buf: break - if buf[-2:] != B_CRLF: - if buf[-1] in B_CRLF: buf = buf[:-1] - buf = buf + B_CRLF + if isinstance(buf, str): + if not buf.endswith(CRLF): + if buf[-1] in CRLF: buf = buf[:-1] + buf = buf + CRLF + buf = bytes(buf, self.encoding) + else: + if not buf.endswith(B_CRLF): + if buf[-1:] in B_CRLF: buf = buf[:-1] + buf = buf + B_CRLF conn.sendall(buf) if callback: callback(buf) conn.close() @@ -771,9 +777,15 @@ while 1: buf = fp.readline() if not buf: break - if buf[-2:] != B_CRLF: - if buf[-1] in B_CRLF: buf = buf[:-1] - buf = buf + B_CRLF + if isinstance(buf, str): + if not buf.endswith(CRLF): + if buf[-1] in CRLF: buf = buf[:-1] + buf = buf + CRLF + buf = bytes(buf, self.encoding) + else: + if not buf.endswith(B_CRLF): + if buf[-1:] in B_CRLF: buf = buf[:-1] + buf = buf + B_CRLF conn.sendall(buf) if callback: callback(buf) # shutdown ssl layer @@ -783,6 +795,7 @@ conn.close() return self.voidresp() + __all__.append('FTP_TLS') all_errors = (Error, IOError, EOFError, ssl.SSLError) Modified: python/branches/py3k/Lib/test/test_ftplib.py ============================================================================== --- python/branches/py3k/Lib/test/test_ftplib.py (original) +++ python/branches/py3k/Lib/test/test_ftplib.py Wed Aug 4 12:12:00 2010 @@ -24,6 +24,7 @@ # the dummy data returned by server over the data channel when # RETR, LIST and NLST commands are issued RETR_DATA = 'abcde12345\r\n' * 1000 +RETR_TEXT = 'abcd\xe912345\r\n' * 1000 LIST_DATA = 'foo\r\nbar\r\n' NLST_DATA = 'foo\r\nbar\r\n' @@ -37,7 +38,7 @@ self.baseclass.last_received_data = '' def handle_read(self): - self.baseclass.last_received_data += self.recv(1024).decode('ascii') + self.baseclass.last_received_data += self.recv(1024).decode('latin-1') def handle_close(self): # XXX: this method can be called many times in a row for a single @@ -49,7 +50,7 @@ self.dtp_conn_closed = True def push(self, what): - super(DummyDTPHandler, self).push(what.encode('ascii')) + super(DummyDTPHandler, self).push(what.encode('latin-1')) def handle_error(self): raise @@ -68,6 +69,7 @@ self.last_received_data = '' self.next_response = '' self.rest = None + self.current_type = 'a' self.push('220 welcome') def collect_incoming_data(self, data): @@ -175,7 +177,16 @@ self.push('257 "pwd ok"') def cmd_type(self, arg): - self.push('200 type ok') + # ASCII type + if arg.lower() == 'a': + self.current_type = 'a' + self.push('200 type ok') + # Binary type + elif arg.lower() == 'i': + self.current_type = 'i' + self.push('200 type ok') + else: + self.push('504 unsupported type') def cmd_quit(self, arg): self.push('221 quit ok') @@ -194,7 +205,10 @@ offset = int(self.rest) else: offset = 0 - self.dtp.push(RETR_DATA[offset:]) + if self.current_type == 'i': + self.dtp.push(RETR_DATA[offset:]) + else: + self.dtp.push(RETR_TEXT[offset:]) self.dtp.close_when_done() self.rest = None @@ -511,7 +525,7 @@ def test_retrlines(self): received = [] self.client.retrlines('retr', received.append) - self.assertEqual(''.join(received), RETR_DATA.replace('\r\n', '')) + self.assertEqual(''.join(received), RETR_TEXT.replace('\r\n', '')) def test_storbinary(self): f = io.BytesIO(RETR_DATA.encode('ascii')) @@ -530,7 +544,7 @@ self.client.storbinary('stor', f, rest=r) self.assertEqual(self.server.handler_instance.rest, str(r)) - def test_storlines(self): + def test_storlines_bytes(self): f = io.BytesIO(RETR_DATA.replace('\r\n', '\n').encode('ascii')) self.client.storlines('stor', f) self.assertEqual(self.server.handler_instance.last_received_data, RETR_DATA) @@ -540,6 +554,16 @@ self.client.storlines('stor foo', f, callback=lambda x: flag.append(None)) self.assertTrue(flag) + def test_storlines_str(self): + f = io.StringIO(RETR_TEXT.replace('\r\n', '\n')) + self.client.storlines('stor', f) + self.assertEqual(self.server.handler_instance.last_received_data, RETR_TEXT) + # test new callback arg + flag = [] + f.seek(0) + self.client.storlines('stor foo', f, callback=lambda x: flag.append(None)) + self.assertTrue(flag) + def test_nlst(self): self.client.nlst() self.assertEqual(self.client.nlst(), NLST_DATA.split('\r\n')[:-1]) Modified: python/branches/py3k/Misc/ACKS ============================================================================== --- python/branches/py3k/Misc/ACKS (original) +++ python/branches/py3k/Misc/ACKS Wed Aug 4 12:12:00 2010 @@ -896,3 +896,4 @@ Tarek Ziad? Peter ?strand Alexander Shigin +Robert DeVaughn Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Wed Aug 4 12:12:00 2010 @@ -37,6 +37,8 @@ Library ------- +- Issue #6822: ftplib's storlines method doesn't work with text files. + - Issue #2944: asyncore doesn't handle connection refused correctly. - Issue #4184: Private attributes on smtpd.SMTPChannel made public and From python-checkins at python.org Wed Aug 4 12:26:30 2010 From: python-checkins at python.org (antoine.pitrou) Date: Wed, 4 Aug 2010 12:26:30 +0200 (CEST) Subject: [Python-checkins] r83709 - python/branches/py3k/Misc/maintainers.rst Message-ID: <20100804102630.7CEE5EE998@mail.python.org> Author: antoine.pitrou Date: Wed Aug 4 12:26:30 2010 New Revision: 83709 Log: Something fun to maintain Modified: python/branches/py3k/Misc/maintainers.rst Modified: python/branches/py3k/Misc/maintainers.rst ============================================================================== --- python/branches/py3k/Misc/maintainers.rst (original) +++ python/branches/py3k/Misc/maintainers.rst Wed Aug 4 12:26:30 2010 @@ -224,7 +224,7 @@ uuid warnings brett.cannon wave -weakref fdrake +weakref fdrake, pitrou webbrowser georg.brandl winreg brian.curtin winsound effbot (inactive) From python-checkins at python.org Wed Aug 4 12:27:17 2010 From: python-checkins at python.org (antoine.pitrou) Date: Wed, 4 Aug 2010 12:27:17 +0200 (CEST) Subject: [Python-checkins] r83710 - in python/branches/release31-maint: Misc/maintainers.rst Message-ID: <20100804102717.475C8EE998@mail.python.org> Author: antoine.pitrou Date: Wed Aug 4 12:27:17 2010 New Revision: 83710 Log: Merged revisions 83709 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83709 | antoine.pitrou | 2010-08-04 12:26:30 +0200 (mer., 04 ao?t 2010) | 3 lines Something fun to maintain ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Misc/maintainers.rst Modified: python/branches/release31-maint/Misc/maintainers.rst ============================================================================== --- python/branches/release31-maint/Misc/maintainers.rst (original) +++ python/branches/release31-maint/Misc/maintainers.rst Wed Aug 4 12:27:17 2010 @@ -224,7 +224,7 @@ uuid warnings brett.cannon wave -weakref fdrake +weakref fdrake, pitrou webbrowser georg.brandl winreg winsound effbot (inactive) From python-checkins at python.org Wed Aug 4 12:27:51 2010 From: python-checkins at python.org (antoine.pitrou) Date: Wed, 4 Aug 2010 12:27:51 +0200 (CEST) Subject: [Python-checkins] r83711 - in python/branches/release27-maint: Misc/maintainers.rst Message-ID: <20100804102751.C93BBEE9B2@mail.python.org> Author: antoine.pitrou Date: Wed Aug 4 12:27:51 2010 New Revision: 83711 Log: Merged revisions 83709 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83709 | antoine.pitrou | 2010-08-04 12:26:30 +0200 (mer., 04 ao?t 2010) | 3 lines Something fun to maintain ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Misc/maintainers.rst Modified: python/branches/release27-maint/Misc/maintainers.rst ============================================================================== --- python/branches/release27-maint/Misc/maintainers.rst (original) +++ python/branches/release27-maint/Misc/maintainers.rst Wed Aug 4 12:27:51 2010 @@ -229,7 +229,7 @@ uuid warnings brett.cannon wave -weakref fdrake +weakref fdrake, pitrou webbrowser georg.brandl winreg winsound effbot (inactive) From python-checkins at python.org Wed Aug 4 12:36:18 2010 From: python-checkins at python.org (giampaolo.rodola) Date: Wed, 4 Aug 2010 12:36:18 +0200 (CEST) Subject: [Python-checkins] r83712 - in python/branches/py3k: Lib/ftplib.py Lib/test/test_ftplib.py Misc/ACKS Misc/NEWS Message-ID: <20100804103618.B4E3CEE9B2@mail.python.org> Author: giampaolo.rodola Date: Wed Aug 4 12:36:18 2010 New Revision: 83712 Log: as per discussion with antoine revert changes made in 83708 as the user useing ftplib's readline methods is supposed to always use a binary file Modified: python/branches/py3k/Lib/ftplib.py python/branches/py3k/Lib/test/test_ftplib.py python/branches/py3k/Misc/ACKS python/branches/py3k/Misc/NEWS Modified: python/branches/py3k/Lib/ftplib.py ============================================================================== --- python/branches/py3k/Lib/ftplib.py (original) +++ python/branches/py3k/Lib/ftplib.py Wed Aug 4 12:36:18 2010 @@ -493,15 +493,9 @@ while 1: buf = fp.readline() if not buf: break - if isinstance(buf, str): - if not buf.endswith(CRLF): - if buf[-1] in CRLF: buf = buf[:-1] - buf = buf + CRLF - buf = bytes(buf, self.encoding) - else: - if not buf.endswith(B_CRLF): - if buf[-1:] in B_CRLF: buf = buf[:-1] - buf = buf + B_CRLF + if buf[-2:] != B_CRLF: + if buf[-1] in B_CRLF: buf = buf[:-1] + buf = buf + B_CRLF conn.sendall(buf) if callback: callback(buf) conn.close() @@ -777,15 +771,9 @@ while 1: buf = fp.readline() if not buf: break - if isinstance(buf, str): - if not buf.endswith(CRLF): - if buf[-1] in CRLF: buf = buf[:-1] - buf = buf + CRLF - buf = bytes(buf, self.encoding) - else: - if not buf.endswith(B_CRLF): - if buf[-1:] in B_CRLF: buf = buf[:-1] - buf = buf + B_CRLF + if buf[-2:] != B_CRLF: + if buf[-1] in B_CRLF: buf = buf[:-1] + buf = buf + B_CRLF conn.sendall(buf) if callback: callback(buf) # shutdown ssl layer @@ -795,7 +783,6 @@ conn.close() return self.voidresp() - __all__.append('FTP_TLS') all_errors = (Error, IOError, EOFError, ssl.SSLError) Modified: python/branches/py3k/Lib/test/test_ftplib.py ============================================================================== --- python/branches/py3k/Lib/test/test_ftplib.py (original) +++ python/branches/py3k/Lib/test/test_ftplib.py Wed Aug 4 12:36:18 2010 @@ -24,7 +24,6 @@ # the dummy data returned by server over the data channel when # RETR, LIST and NLST commands are issued RETR_DATA = 'abcde12345\r\n' * 1000 -RETR_TEXT = 'abcd\xe912345\r\n' * 1000 LIST_DATA = 'foo\r\nbar\r\n' NLST_DATA = 'foo\r\nbar\r\n' @@ -38,7 +37,7 @@ self.baseclass.last_received_data = '' def handle_read(self): - self.baseclass.last_received_data += self.recv(1024).decode('latin-1') + self.baseclass.last_received_data += self.recv(1024).decode('ascii') def handle_close(self): # XXX: this method can be called many times in a row for a single @@ -50,7 +49,7 @@ self.dtp_conn_closed = True def push(self, what): - super(DummyDTPHandler, self).push(what.encode('latin-1')) + super(DummyDTPHandler, self).push(what.encode('ascii')) def handle_error(self): raise @@ -69,7 +68,6 @@ self.last_received_data = '' self.next_response = '' self.rest = None - self.current_type = 'a' self.push('220 welcome') def collect_incoming_data(self, data): @@ -177,16 +175,7 @@ self.push('257 "pwd ok"') def cmd_type(self, arg): - # ASCII type - if arg.lower() == 'a': - self.current_type = 'a' - self.push('200 type ok') - # Binary type - elif arg.lower() == 'i': - self.current_type = 'i' - self.push('200 type ok') - else: - self.push('504 unsupported type') + self.push('200 type ok') def cmd_quit(self, arg): self.push('221 quit ok') @@ -205,10 +194,7 @@ offset = int(self.rest) else: offset = 0 - if self.current_type == 'i': - self.dtp.push(RETR_DATA[offset:]) - else: - self.dtp.push(RETR_TEXT[offset:]) + self.dtp.push(RETR_DATA[offset:]) self.dtp.close_when_done() self.rest = None @@ -525,7 +511,7 @@ def test_retrlines(self): received = [] self.client.retrlines('retr', received.append) - self.assertEqual(''.join(received), RETR_TEXT.replace('\r\n', '')) + self.assertEqual(''.join(received), RETR_DATA.replace('\r\n', '')) def test_storbinary(self): f = io.BytesIO(RETR_DATA.encode('ascii')) @@ -544,7 +530,7 @@ self.client.storbinary('stor', f, rest=r) self.assertEqual(self.server.handler_instance.rest, str(r)) - def test_storlines_bytes(self): + def test_storlines(self): f = io.BytesIO(RETR_DATA.replace('\r\n', '\n').encode('ascii')) self.client.storlines('stor', f) self.assertEqual(self.server.handler_instance.last_received_data, RETR_DATA) @@ -554,16 +540,6 @@ self.client.storlines('stor foo', f, callback=lambda x: flag.append(None)) self.assertTrue(flag) - def test_storlines_str(self): - f = io.StringIO(RETR_TEXT.replace('\r\n', '\n')) - self.client.storlines('stor', f) - self.assertEqual(self.server.handler_instance.last_received_data, RETR_TEXT) - # test new callback arg - flag = [] - f.seek(0) - self.client.storlines('stor foo', f, callback=lambda x: flag.append(None)) - self.assertTrue(flag) - def test_nlst(self): self.client.nlst() self.assertEqual(self.client.nlst(), NLST_DATA.split('\r\n')[:-1]) Modified: python/branches/py3k/Misc/ACKS ============================================================================== --- python/branches/py3k/Misc/ACKS (original) +++ python/branches/py3k/Misc/ACKS Wed Aug 4 12:36:18 2010 @@ -896,4 +896,3 @@ Tarek Ziad? Peter ?strand Alexander Shigin -Robert DeVaughn Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Wed Aug 4 12:36:18 2010 @@ -37,8 +37,6 @@ Library ------- -- Issue #6822: ftplib's storlines method doesn't work with text files. - - Issue #2944: asyncore doesn't handle connection refused correctly. - Issue #4184: Private attributes on smtpd.SMTPChannel made public and From python-checkins at python.org Wed Aug 4 13:48:56 2010 From: python-checkins at python.org (antoine.pitrou) Date: Wed, 4 Aug 2010 13:48:56 +0200 (CEST) Subject: [Python-checkins] r83713 - in python/branches/py3k/Lib/test: support.py test_subprocess.py test_threading.py Message-ID: <20100804114856.8C0C6EEA60@mail.python.org> Author: antoine.pitrou Date: Wed Aug 4 13:48:56 2010 New Revision: 83713 Log: Factor out stripping of interpreter debug output in test.support.strip_python_stderr() Modified: python/branches/py3k/Lib/test/support.py python/branches/py3k/Lib/test/test_subprocess.py python/branches/py3k/Lib/test/test_threading.py Modified: python/branches/py3k/Lib/test/support.py ============================================================================== --- python/branches/py3k/Lib/test/support.py (original) +++ python/branches/py3k/Lib/test/support.py Wed Aug 4 13:48:56 2010 @@ -1243,3 +1243,13 @@ yield finally: del obj[item] + +def strip_python_stderr(stderr): + """Strip the stderr of a Python process from potential debug output + emitted by the interpreter. + + This will typically be run on the result of the communicate() method + of a subprocess.Popen object. + """ + stderr = re.sub(br"\[\d+ refs\]\r?\n?$", b"", stderr).strip() + return stderr Modified: python/branches/py3k/Lib/test/test_subprocess.py ============================================================================== --- python/branches/py3k/Lib/test/test_subprocess.py (original) +++ python/branches/py3k/Lib/test/test_subprocess.py Wed Aug 4 13:48:56 2010 @@ -53,7 +53,7 @@ # In a debug build, stuff like "[6580 refs]" is printed to stderr at # shutdown time. That frustrates tests trying to check stderr produced # from a spawned Python process. - actual = re.sub("\[\d+ refs\]\r?\n?$", "", stderr.decode()).encode() + actual = support.strip_python_stderr(stderr) self.assertEqual(actual, expected, msg) Modified: python/branches/py3k/Lib/test/test_threading.py ============================================================================== --- python/branches/py3k/Lib/test/test_threading.py (original) +++ python/branches/py3k/Lib/test/test_threading.py Wed Aug 4 13:48:56 2010 @@ -1,7 +1,7 @@ # Very rudimentary test of threading module import test.support -from test.support import verbose +from test.support import verbose, strip_python_stderr import random import re import sys @@ -350,7 +350,7 @@ stdout, stderr = p.communicate() self.assertEqual(stdout.strip(), b"Woke up, sleep function is: ") - stderr = re.sub(br"^\[\d+ refs\]", b"", stderr, re.MULTILINE).strip() + stderr = strip_python_stderr(stderr) self.assertEqual(stderr, b"") def test_enumerate_after_join(self): From python-checkins at python.org Wed Aug 4 14:27:36 2010 From: python-checkins at python.org (richard.jones) Date: Wed, 4 Aug 2010 14:27:36 +0200 (CEST) Subject: [Python-checkins] r83714 - in python/branches/py3k/Lib/test: mock_socket.py test_smtpd.py test_smtplib.py Message-ID: <20100804122736.706DBEE9A0@mail.python.org> Author: richard.jones Date: Wed Aug 4 14:27:36 2010 New Revision: 83714 Log: fix test_smtplib/test_smtpd collision through pre-loaded reply data in mock_socket Modified: python/branches/py3k/Lib/test/mock_socket.py python/branches/py3k/Lib/test/test_smtpd.py python/branches/py3k/Lib/test/test_smtplib.py Modified: python/branches/py3k/Lib/test/mock_socket.py ============================================================================== --- python/branches/py3k/Lib/test/mock_socket.py (original) +++ python/branches/py3k/Lib/test/mock_socket.py Wed Aug 4 14:27:36 2010 @@ -36,6 +36,7 @@ self.lines = [] if _reply_data: self.lines.append(_reply_data) + _reply_data = None self.conn = None self.timeout = None Modified: python/branches/py3k/Lib/test/test_smtpd.py ============================================================================== --- python/branches/py3k/Lib/test/test_smtpd.py (original) +++ python/branches/py3k/Lib/test/test_smtpd.py Wed Aug 4 14:27:36 2010 @@ -189,8 +189,8 @@ self.write_line(b'RCPT To:ham at example') self.write_line(b'DATA') self.write_line(b'data\r\n.') - self.assertEqual(self.server.messages[-1], - ('peer', 'eggs at example', ['spam at example','ham at example'], 'data')) + self.assertEqual(self.server.messages, + [('peer', 'eggs at example', ['spam at example','ham at example'], 'data')]) def test_manual_status(self): # checks that the Channel is able to return a custom status message @@ -209,8 +209,8 @@ self.write_line(b'RCPT To:eggs at example') self.write_line(b'DATA') self.write_line(b'data\r\n.') - self.assertEqual(self.server.messages[0], - ('peer', 'foo at example', ['eggs at example'], 'data')) + self.assertEqual(self.server.messages, + [('peer', 'foo at example', ['eggs at example'], 'data')]) def test_RSET_syntax(self): self.write_line(b'RSET hi') Modified: python/branches/py3k/Lib/test/test_smtplib.py ============================================================================== --- python/branches/py3k/Lib/test/test_smtplib.py (original) +++ python/branches/py3k/Lib/test/test_smtplib.py Wed Aug 4 14:27:36 2010 @@ -64,17 +64,20 @@ smtp.close() def testBasic2(self): + mock_socket.reply_with(b"220 Hola mundo") # connects, include port in host name smtp = smtplib.SMTP("%s:%s" % (HOST, self.port)) smtp.close() def testLocalHostName(self): + mock_socket.reply_with(b"220 Hola mundo") # check that supplied local_hostname is used smtp = smtplib.SMTP(HOST, self.port, local_hostname="testhost") self.assertEqual(smtp.local_hostname, "testhost") smtp.close() def testTimeoutDefault(self): + mock_socket.reply_with(b"220 Hola mundo") self.assertTrue(mock_socket.getdefaulttimeout() is None) mock_socket.setdefaulttimeout(30) self.assertEqual(mock_socket.getdefaulttimeout(), 30) @@ -86,6 +89,7 @@ smtp.close() def testTimeoutNone(self): + mock_socket.reply_with(b"220 Hola mundo") self.assertTrue(socket.getdefaulttimeout() is None) socket.setdefaulttimeout(30) try: @@ -96,6 +100,7 @@ smtp.close() def testTimeoutValue(self): + mock_socket.reply_with(b"220 Hola mundo") smtp = smtplib.SMTP(HOST, self.port, timeout=30) self.assertEqual(smtp.sock.gettimeout(), 30) smtp.close() From python-checkins at python.org Wed Aug 4 15:24:41 2010 From: python-checkins at python.org (antoine.pitrou) Date: Wed, 4 Aug 2010 15:24:41 +0200 (CEST) Subject: [Python-checkins] r83715 - python/branches/py3k/Misc/maintainers.rst Message-ID: <20100804132441.A03A0EE998@mail.python.org> Author: antoine.pitrou Date: Wed Aug 4 15:24:41 2010 New Revision: 83715 Log: I'm interested in threading issues as well Modified: python/branches/py3k/Misc/maintainers.rst Modified: python/branches/py3k/Misc/maintainers.rst ============================================================================== --- python/branches/py3k/Misc/maintainers.rst (original) +++ python/branches/py3k/Misc/maintainers.rst Wed Aug 4 15:24:41 2010 @@ -42,7 +42,7 @@ __future__ __main__ gvanrossum _dummy_thread brett.cannon -_thread +_thread pitrou abc aifc r.david.murray argparse bethard @@ -206,7 +206,7 @@ termios test textwrap -threading +threading pitrou time alexander.belopolsky timeit tkinter gpolo From python-checkins at python.org Wed Aug 4 15:25:07 2010 From: python-checkins at python.org (antoine.pitrou) Date: Wed, 4 Aug 2010 15:25:07 +0200 (CEST) Subject: [Python-checkins] r83716 - in python/branches/release31-maint: Misc/maintainers.rst Message-ID: <20100804132507.C7568EEA19@mail.python.org> Author: antoine.pitrou Date: Wed Aug 4 15:25:07 2010 New Revision: 83716 Log: Merged revisions 83715 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83715 | antoine.pitrou | 2010-08-04 15:24:41 +0200 (mer., 04 ao?t 2010) | 3 lines I'm interested in threading issues as well ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Misc/maintainers.rst Modified: python/branches/release31-maint/Misc/maintainers.rst ============================================================================== --- python/branches/release31-maint/Misc/maintainers.rst (original) +++ python/branches/release31-maint/Misc/maintainers.rst Wed Aug 4 15:25:07 2010 @@ -42,7 +42,7 @@ __future__ __main__ gvanrossum _dummy_thread brett.cannon -_thread +_thread pitrou abc aifc r.david.murray argparse bethard @@ -206,7 +206,7 @@ termios test textwrap -threading +threading pitrou time alexander.belopolsky timeit tkinter gpolo From python-checkins at python.org Wed Aug 4 15:25:45 2010 From: python-checkins at python.org (antoine.pitrou) Date: Wed, 4 Aug 2010 15:25:45 +0200 (CEST) Subject: [Python-checkins] r83717 - in python/branches/release27-maint: Misc/maintainers.rst Message-ID: <20100804132545.36AE4EEA87@mail.python.org> Author: antoine.pitrou Date: Wed Aug 4 15:25:45 2010 New Revision: 83717 Log: Merged revisions 83715 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83715 | antoine.pitrou | 2010-08-04 15:24:41 +0200 (mer., 04 ao?t 2010) | 3 lines I'm interested in threading issues as well ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Misc/maintainers.rst Modified: python/branches/release27-maint/Misc/maintainers.rst ============================================================================== --- python/branches/release27-maint/Misc/maintainers.rst (original) +++ python/branches/release27-maint/Misc/maintainers.rst Wed Aug 4 15:25:45 2010 @@ -43,7 +43,7 @@ __future__ __main__ gvanrossum _dummy_thread brett.cannon -_thread +_thread pitrou abc aifc r.david.murray argparse bethard @@ -211,7 +211,7 @@ termios test textwrap -threading +threading pitrou time alexander.belopolsky timeit Tkinter gpolo From barry at python.org Wed Aug 4 16:41:36 2010 From: barry at python.org (Barry Warsaw) Date: Wed, 4 Aug 2010 10:41:36 -0400 Subject: [Python-checkins] r83704 - in python/branches/release26-maint: Lib/asyncore.py Misc/ACKS Misc/NEWS In-Reply-To: <20100804085838.3D95EEE994@mail.python.org> References: <20100804085838.3D95EEE994@mail.python.org> Message-ID: <20100804104136.76a30782@heresy> Hi Giampaolo, Now that we're in quasi-freeze for 2.6.6 final, this is the kind of change I'd like to review before backporting. In this case, I'll let it through, but please check with me first next time. And thanks for your work! -Barry On Aug 04, 2010, at 10:58 AM, giampaolo.rodola wrote: >Author: giampaolo.rodola >Date: Wed Aug 4 10:58:38 2010 >New Revision: 83704 > >Log: >Merged revisions 83703 via svnmerge from >svn+ssh://pythondev at svn.python.org/python/branches/release27-maint > >........ > r83703 | giampaolo.rodola | 2010-08-04 10:35:25 +0200 (mer, 04 ago > 2010) | 1 line > fix issue #2944: asyncore doesn't handle connection refused > correctly (patch by Alexander Shigin) >........ > > >Modified: > python/branches/release26-maint/ (props changed) > python/branches/release26-maint/Lib/asyncore.py > python/branches/release26-maint/Misc/ACKS > python/branches/release26-maint/Misc/NEWS > >Modified: python/branches/release26-maint/Lib/asyncore.py >============================================================================== >--- python/branches/release26-maint/Lib/asyncore.py (original) >+++ python/branches/release26-maint/Lib/asyncore.py Wed Aug 4 >10:58:38 2010 @@ -422,8 +422,11 @@ > self.handle_read() > > def handle_connect_event(self): >- self.connected = True >+ err = self.socket.getsockopt(socket.SOL_SOCKET, >socket.SO_ERROR) >+ if err != 0: >+ raise socket.error(err, _strerror(err)) > self.handle_connect() >+ self.connected = True > > def handle_write_event(self): > if self.accepting: > >Modified: python/branches/release26-maint/Misc/ACKS >============================================================================== >--- python/branches/release26-maint/Misc/ACKS (original) >+++ python/branches/release26-maint/Misc/ACKS Wed Aug 4 >10:58:38 2010 @@ -817,3 +817,4 @@ > Peter ?strand > Jesse Noller > Fredrik H??rd >+Alexander Shigin > >Modified: python/branches/release26-maint/Misc/NEWS >============================================================================== >--- python/branches/release26-maint/Misc/NEWS (original) >+++ python/branches/release26-maint/Misc/NEWS Wed Aug 4 >10:58:38 2010 @@ -89,6 +89,8 @@ > Library > ------- > >+- Issue #2944: asyncore doesn't handle connection refused correctly. >+ > - Issue #8447: Make distutils.sysconfig follow symlinks in the path to > the interpreter executable. This fixes a failure of > test_httpservers on OS X. -------------- next part -------------- A non-text attachment was scrubbed... Name: signature.asc Type: application/pgp-signature Size: 836 bytes Desc: not available URL: From python-checkins at python.org Wed Aug 4 16:42:13 2010 From: python-checkins at python.org (mark.dickinson) Date: Wed, 4 Aug 2010 16:42:13 +0200 (CEST) Subject: [Python-checkins] r83718 - in python/branches/release26-maint: Lib/asyncore.py Misc/NEWS Message-ID: <20100804144213.AC2ECEEA77@mail.python.org> Author: mark.dickinson Date: Wed Aug 4 16:42:13 2010 New Revision: 83718 Log: Issue #5798: Handle select.poll flag oddities properly on OS X. This fixes test_asynchat and test_smtplib failures on OS X. (Backport of r73182 from trunk.) Modified: python/branches/release26-maint/Lib/asyncore.py python/branches/release26-maint/Misc/NEWS Modified: python/branches/release26-maint/Lib/asyncore.py ============================================================================== --- python/branches/release26-maint/Lib/asyncore.py (original) +++ python/branches/release26-maint/Lib/asyncore.py Wed Aug 4 16:42:13 2010 @@ -103,10 +103,16 @@ obj.handle_read_event() if flags & select.POLLOUT: obj.handle_write_event() - if flags & (select.POLLHUP | select.POLLERR | select.POLLNVAL): - obj.handle_close() if flags & select.POLLPRI: obj.handle_expt_event() + if flags & (select.POLLHUP | select.POLLERR | select.POLLNVAL): + obj.handle_close() + except socket.error, e: + if e.args[0] not in (EBADF, ECONNRESET, ENOTCONN, ESHUTDOWN, +ECONNABORTED): + obj.handle_error() + else: + obj.handle_close() except _reraised_exceptions: raise except: Modified: python/branches/release26-maint/Misc/NEWS ============================================================================== --- python/branches/release26-maint/Misc/NEWS (original) +++ python/branches/release26-maint/Misc/NEWS Wed Aug 4 16:42:13 2010 @@ -4,6 +4,18 @@ (editors: check NEWS.help for information about editing NEWS using ReST.) +What's New in Python 2.6.6? +=========================== + +*Release date: XXXX-XX-XX* + +Library +------- + +- Issue #5798: Handle select.poll flag oddities properly on OS X. + This fixes test_asynchat and test_smtplib failures on OS X. + + What's New in Python 2.6.6 rc 1? ================================ From python-checkins at python.org Wed Aug 4 17:43:16 2010 From: python-checkins at python.org (antoine.pitrou) Date: Wed, 4 Aug 2010 17:43:16 +0200 (CEST) Subject: [Python-checkins] r83719 - in python/branches/py3k: Lib/test/test_rlcompleter.py Lib/test/test_sundry.py Misc/ACKS Misc/NEWS Message-ID: <20100804154316.4F55DEEAAA@mail.python.org> Author: antoine.pitrou Date: Wed Aug 4 17:43:16 2010 New Revision: 83719 Log: Issue #9496: Provide a test suite for the rlcompleter module. Patch by Michele Orr?. Added: python/branches/py3k/Lib/test/test_rlcompleter.py Modified: python/branches/py3k/Lib/test/test_sundry.py python/branches/py3k/Misc/ACKS python/branches/py3k/Misc/NEWS Added: python/branches/py3k/Lib/test/test_rlcompleter.py ============================================================================== --- (empty file) +++ python/branches/py3k/Lib/test/test_rlcompleter.py Wed Aug 4 17:43:16 2010 @@ -0,0 +1,73 @@ +from test import support +import unittest +import builtins +import rlcompleter + +class CompleteMe: + """ Trivial class used in testing rlcompleter.Completer. """ + spam = 1 + + +class TestRlcompleter(unittest.TestCase): + def setUp(self): + self.stdcompleter = rlcompleter.Completer() + self.completer = rlcompleter.Completer(dict(spam=int, + egg=str, + CompleteMe=CompleteMe)) + + # forces stdcompleter to bind builtins namespace + self.stdcompleter.complete('', 0) + + def test_namespace(self): + class A(dict): + pass + class B(list): + pass + + self.assertTrue(self.stdcompleter.use_main_ns) + self.assertFalse(self.completer.use_main_ns) + self.assertFalse(rlcompleter.Completer(A()).use_main_ns) + self.assertRaises(TypeError, rlcompleter.Completer, B((1,))) + + def test_global_matches(self): + # test with builtins namespace + self.assertEqual(self.stdcompleter.global_matches('di'), + [x+'(' for x in dir(builtins) if x.startswith('di')]) + self.assertEqual(self.stdcompleter.global_matches('st'), + [x+'(' for x in dir(builtins) if x.startswith('st')]) + self.assertEqual(self.stdcompleter.global_matches('akaksajadhak'), []) + + # test with a customized namespace + self.assertEqual(self.completer.global_matches('CompleteM'), + ['CompleteMe(']) + self.assertEqual(self.completer.global_matches('eg'), + ['egg(']) + # XXX: see issue5256 + self.assertEqual(self.completer.global_matches('CompleteM'), + ['CompleteMe(']) + + def test_attr_matches(self): + # test with builtins namespace + self.assertEqual(self.stdcompleter.attr_matches('str.s'), + ['str.{}('.format(x) for x in dir(str) + if x.startswith('s')]) + self.assertEqual(self.stdcompleter.attr_matches('tuple.foospamegg'), []) + + # test with a customized namespace + self.assertEqual(self.completer.attr_matches('CompleteMe.sp'), + ['CompleteMe.spam']) + self.assertEqual(self.completer.attr_matches('Completeme.egg'), []) + + CompleteMe.me = CompleteMe + self.assertEqual(self.completer.attr_matches('CompleteMe.me.me.sp'), + ['CompleteMe.me.me.spam']) + self.assertEqual(self.completer.attr_matches('egg.s'), + ['egg.{}('.format(x) for x in dir(str) + if x.startswith('s')]) + +def test_main(): + support.run_unittest(TestRlcompleter) + + +if __name__ == '__main__': + test_main() Modified: python/branches/py3k/Lib/test/test_sundry.py ============================================================================== --- python/branches/py3k/Lib/test/test_sundry.py (original) +++ python/branches/py3k/Lib/test/test_sundry.py Wed Aug 4 17:43:16 2010 @@ -56,7 +56,6 @@ import os2emxpath import pstats import py_compile - import rlcompleter import sndhdr import symbol import tabnanny Modified: python/branches/py3k/Misc/ACKS ============================================================================== --- python/branches/py3k/Misc/ACKS (original) +++ python/branches/py3k/Misc/ACKS Wed Aug 4 17:43:16 2010 @@ -591,6 +591,7 @@ Piet van Oostrum Jason Orendorff Douglas Orr +Michele Orr? Denis S. Otkidach Michael Otteneder R. M. Oudkerk Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Wed Aug 4 17:43:16 2010 @@ -108,6 +108,9 @@ Tests ----- +- Issue #9496: Provide a test suite for the rlcompleter module. Patch by + Michele Orr?. + - Issue #8687: provide a test suite for sched.py module. From python-checkins at python.org Wed Aug 4 17:46:18 2010 From: python-checkins at python.org (martin.v.loewis) Date: Wed, 4 Aug 2010 17:46:18 +0200 (CEST) Subject: [Python-checkins] r83720 - python/branches/release26-maint/Tools/msi/uuids.py Message-ID: <20100804154618.18239EEAAF@mail.python.org> Author: martin.v.loewis Date: Wed Aug 4 17:46:17 2010 New Revision: 83720 Log: Add UUIDs for 2.6.6. Modified: python/branches/release26-maint/Tools/msi/uuids.py Modified: python/branches/release26-maint/Tools/msi/uuids.py ============================================================================== --- python/branches/release26-maint/Tools/msi/uuids.py (original) +++ python/branches/release26-maint/Tools/msi/uuids.py Wed Aug 4 17:46:17 2010 @@ -58,4 +58,6 @@ '2.6.5121':'{e0e273d7-7598-4701-8325-c90c069fd5ff}', # 2.6.5c1 '2.6.5122':'{fa227b76-0671-4dc6-b826-c2ff2a70dfd5}', # 2.6.5c2 '2.6.5150':'{4723f199-fa64-4233-8e6e-9fccc95a18ee}', # 2.6.5 + '2.6.6121':'{399b7502-8119-4400-9218-4906194fedf5}', # 2.6.6rc1 + '2.6.6150':'{6151cf20-0bd8-4023-a4a0-6a86dcfe58e5}', # 2.6.6 } From python-checkins at python.org Wed Aug 4 17:47:24 2010 From: python-checkins at python.org (martin.v.loewis) Date: Wed, 4 Aug 2010 17:47:24 +0200 (CEST) Subject: [Python-checkins] r83721 - python/branches/release26-maint/Tools/msi/msi.py Message-ID: <20100804154724.39C45EEA77@mail.python.org> Author: martin.v.loewis Date: Wed Aug 4 17:47:24 2010 New Revision: 83721 Log: Issue #8241: Avoid compiling py2_test_grammar. Modified: python/branches/release26-maint/Tools/msi/msi.py Modified: python/branches/release26-maint/Tools/msi/msi.py ============================================================================== --- python/branches/release26-maint/Tools/msi/msi.py (original) +++ python/branches/release26-maint/Tools/msi/msi.py Wed Aug 4 17:47:24 2010 @@ -398,7 +398,7 @@ ("VerdanaRed9", "Verdana", 9, 255, 0), ]) - compileargs = r'-Wi "[TARGETDIR]Lib\compileall.py" -f -x "bad_coding|badsyntax|site-packages|py3_" "[TARGETDIR]Lib"' + compileargs = r'-Wi "[TARGETDIR]Lib\compileall.py" -f -x "bad_coding|badsyntax|site-packages|py3_|py2_test_grammar" "[TARGETDIR]Lib"' lib2to3args = r'-c "import lib2to3.pygram, lib2to3.patcomp;lib2to3.patcomp.PatternCompiler()"' # See "CustomAction Table" add_data(db, "CustomAction", [ From python-checkins at python.org Wed Aug 4 17:47:24 2010 From: python-checkins at python.org (brian.curtin) Date: Wed, 4 Aug 2010 17:47:24 +0200 (CEST) Subject: [Python-checkins] r83722 - in python/branches/py3k/Lib/multiprocessing: connection.py forking.py heap.py reduction.py Message-ID: <20100804154724.83634EEA76@mail.python.org> Author: brian.curtin Date: Wed Aug 4 17:47:24 2010 New Revision: 83722 Log: Fix #9513 to remove relative imports from multiprocessing. The test suite currently skips test_multiprocessing on Windows because it fails on finding _multiprocessing in several win32-specific blocks. Removing the relative import lets this through and allows the test to run (and pass). Modified: python/branches/py3k/Lib/multiprocessing/connection.py python/branches/py3k/Lib/multiprocessing/forking.py python/branches/py3k/Lib/multiprocessing/heap.py python/branches/py3k/Lib/multiprocessing/reduction.py Modified: python/branches/py3k/Lib/multiprocessing/connection.py ============================================================================== --- python/branches/py3k/Lib/multiprocessing/connection.py (original) +++ python/branches/py3k/Lib/multiprocessing/connection.py Wed Aug 4 17:47:24 2010 @@ -173,7 +173,7 @@ else: - from ._multiprocessing import win32 + from _multiprocessing import win32 def Pipe(duplex=True): ''' Modified: python/branches/py3k/Lib/multiprocessing/forking.py ============================================================================== --- python/branches/py3k/Lib/multiprocessing/forking.py (original) +++ python/branches/py3k/Lib/multiprocessing/forking.py Wed Aug 4 17:47:24 2010 @@ -157,7 +157,7 @@ import time from pickle import dump, load, HIGHEST_PROTOCOL - from ._multiprocessing import win32, Connection, PipeConnection + from _multiprocessing import win32, Connection, PipeConnection from .util import Finalize def dump(obj, file, protocol=None): Modified: python/branches/py3k/Lib/multiprocessing/heap.py ============================================================================== --- python/branches/py3k/Lib/multiprocessing/heap.py (original) +++ python/branches/py3k/Lib/multiprocessing/heap.py Wed Aug 4 17:47:24 2010 @@ -26,7 +26,7 @@ if sys.platform == 'win32': - from ._multiprocessing import win32 + from _multiprocessing import win32 class Arena(object): Modified: python/branches/py3k/Lib/multiprocessing/reduction.py ============================================================================== --- python/branches/py3k/Lib/multiprocessing/reduction.py (original) +++ python/branches/py3k/Lib/multiprocessing/reduction.py Wed Aug 4 17:47:24 2010 @@ -34,7 +34,7 @@ if sys.platform == 'win32': import _subprocess - from ._multiprocessing import win32 + from _multiprocessing import win32 def send_handle(conn, handle, destination_pid): process_handle = win32.OpenProcess( From python-checkins at python.org Wed Aug 4 17:48:41 2010 From: python-checkins at python.org (antoine.pitrou) Date: Wed, 4 Aug 2010 17:48:41 +0200 (CEST) Subject: [Python-checkins] r83723 - in python/branches/release31-maint: Lib/test/test_rlcompleter.py Lib/test/test_sundry.py Misc/ACKS Misc/NEWS Message-ID: <20100804154841.50ABBEEAAE@mail.python.org> Author: antoine.pitrou Date: Wed Aug 4 17:48:41 2010 New Revision: 83723 Log: Merged revisions 83719 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83719 | antoine.pitrou | 2010-08-04 17:43:16 +0200 (mer., 04 ao?t 2010) | 4 lines Issue #9496: Provide a test suite for the rlcompleter module. Patch by Michele Orr?. ........ Added: python/branches/release31-maint/Lib/test/test_rlcompleter.py - copied unchanged from r83719, /python/branches/py3k/Lib/test/test_rlcompleter.py Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Lib/test/test_sundry.py python/branches/release31-maint/Misc/ACKS python/branches/release31-maint/Misc/NEWS Modified: python/branches/release31-maint/Lib/test/test_sundry.py ============================================================================== --- python/branches/release31-maint/Lib/test/test_sundry.py (original) +++ python/branches/release31-maint/Lib/test/test_sundry.py Wed Aug 4 17:48:41 2010 @@ -59,7 +59,6 @@ import pdb import pstats import py_compile - import rlcompleter import sched import sndhdr import symbol Modified: python/branches/release31-maint/Misc/ACKS ============================================================================== --- python/branches/release31-maint/Misc/ACKS (original) +++ python/branches/release31-maint/Misc/ACKS Wed Aug 4 17:48:41 2010 @@ -561,6 +561,7 @@ Piet van Oostrum Jason Orendorff Douglas Orr +Michele Orr? Denis S. Otkidach Michael Otteneder R. M. Oudkerk Modified: python/branches/release31-maint/Misc/NEWS ============================================================================== --- python/branches/release31-maint/Misc/NEWS (original) +++ python/branches/release31-maint/Misc/NEWS Wed Aug 4 17:48:41 2010 @@ -437,6 +437,9 @@ Tests ----- +- Issue #9496: Provide a test suite for the rlcompleter module. Patch by + Michele Orr?. + - Issue #9251: test_threaded_import didn't fail when run through regrtest if the import lock was disabled. From python-checkins at python.org Wed Aug 4 17:54:20 2010 From: python-checkins at python.org (brian.curtin) Date: Wed, 4 Aug 2010 17:54:20 +0200 (CEST) Subject: [Python-checkins] r83724 - in python/branches/release31-maint/Lib/multiprocessing: connection.py forking.py heap.py reduction.py Message-ID: <20100804155420.1DC6CEEA95@mail.python.org> Author: brian.curtin Date: Wed Aug 4 17:54:19 2010 New Revision: 83724 Log: Merged revisions 83722 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83722 | brian.curtin | 2010-08-04 10:47:24 -0500 (Wed, 04 Aug 2010) | 7 lines Fix #9513 to remove relative imports from multiprocessing. The test suite currently skips test_multiprocessing on Windows because it fails on finding _multiprocessing in several win32-specific blocks. Removing the relative import lets this through and allows the test to run (and pass). ........ Modified: python/branches/release31-maint/Lib/multiprocessing/connection.py python/branches/release31-maint/Lib/multiprocessing/forking.py python/branches/release31-maint/Lib/multiprocessing/heap.py python/branches/release31-maint/Lib/multiprocessing/reduction.py Modified: python/branches/release31-maint/Lib/multiprocessing/connection.py ============================================================================== --- python/branches/release31-maint/Lib/multiprocessing/connection.py (original) +++ python/branches/release31-maint/Lib/multiprocessing/connection.py Wed Aug 4 17:54:19 2010 @@ -173,7 +173,7 @@ else: - from ._multiprocessing import win32 + from _multiprocessing import win32 def Pipe(duplex=True): ''' Modified: python/branches/release31-maint/Lib/multiprocessing/forking.py ============================================================================== --- python/branches/release31-maint/Lib/multiprocessing/forking.py (original) +++ python/branches/release31-maint/Lib/multiprocessing/forking.py Wed Aug 4 17:54:19 2010 @@ -157,7 +157,7 @@ import time from pickle import dump, load, HIGHEST_PROTOCOL - from ._multiprocessing import win32, Connection, PipeConnection + from _multiprocessing import win32, Connection, PipeConnection from .util import Finalize def dump(obj, file, protocol=None): Modified: python/branches/release31-maint/Lib/multiprocessing/heap.py ============================================================================== --- python/branches/release31-maint/Lib/multiprocessing/heap.py (original) +++ python/branches/release31-maint/Lib/multiprocessing/heap.py Wed Aug 4 17:54:19 2010 @@ -26,7 +26,7 @@ if sys.platform == 'win32': - from ._multiprocessing import win32 + from _multiprocessing import win32 class Arena(object): Modified: python/branches/release31-maint/Lib/multiprocessing/reduction.py ============================================================================== --- python/branches/release31-maint/Lib/multiprocessing/reduction.py (original) +++ python/branches/release31-maint/Lib/multiprocessing/reduction.py Wed Aug 4 17:54:19 2010 @@ -34,7 +34,7 @@ if sys.platform == 'win32': import _subprocess - from ._multiprocessing import win32 + from _multiprocessing import win32 def send_handle(conn, handle, destination_pid): process_handle = win32.OpenProcess( From python-checkins at python.org Wed Aug 4 17:54:33 2010 From: python-checkins at python.org (antoine.pitrou) Date: Wed, 4 Aug 2010 17:54:33 +0200 (CEST) Subject: [Python-checkins] r83725 - in python/branches/release27-maint: Lib/test/test_rlcompleter.py Lib/test/test_sundry.py Misc/ACKS Misc/NEWS Message-ID: <20100804155433.51A8EEEA9B@mail.python.org> Author: antoine.pitrou Date: Wed Aug 4 17:54:33 2010 New Revision: 83725 Log: Merged revisions 83719 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83719 | antoine.pitrou | 2010-08-04 17:43:16 +0200 (mer., 04 ao?t 2010) | 4 lines Issue #9496: Provide a test suite for the rlcompleter module. Patch by Michele Orr?. ........ Added: python/branches/release27-maint/Lib/test/test_rlcompleter.py - copied, changed from r83719, /python/branches/py3k/Lib/test/test_rlcompleter.py Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Lib/test/test_sundry.py python/branches/release27-maint/Misc/ACKS python/branches/release27-maint/Misc/NEWS Copied: python/branches/release27-maint/Lib/test/test_rlcompleter.py (from r83719, /python/branches/py3k/Lib/test/test_rlcompleter.py) ============================================================================== --- /python/branches/py3k/Lib/test/test_rlcompleter.py (original) +++ python/branches/release27-maint/Lib/test/test_rlcompleter.py Wed Aug 4 17:54:33 2010 @@ -1,9 +1,9 @@ -from test import support +from test import test_support as support import unittest -import builtins +import __builtin__ as builtins import rlcompleter -class CompleteMe: +class CompleteMe(object): """ Trivial class used in testing rlcompleter.Completer. """ spam = 1 Modified: python/branches/release27-maint/Lib/test/test_sundry.py ============================================================================== --- python/branches/release27-maint/Lib/test/test_sundry.py (original) +++ python/branches/release27-maint/Lib/test/test_sundry.py Wed Aug 4 17:54:33 2010 @@ -65,7 +65,6 @@ import pstats import py_compile import rexec - import rlcompleter import sched import sndhdr import statvfs Modified: python/branches/release27-maint/Misc/ACKS ============================================================================== --- python/branches/release27-maint/Misc/ACKS (original) +++ python/branches/release27-maint/Misc/ACKS Wed Aug 4 17:54:33 2010 @@ -575,6 +575,7 @@ Piet van Oostrum Jason Orendorff Douglas Orr +Michele Orr? Denis S. Otkidach Michael Otteneder R. M. Oudkerk Modified: python/branches/release27-maint/Misc/NEWS ============================================================================== --- python/branches/release27-maint/Misc/NEWS (original) +++ python/branches/release27-maint/Misc/NEWS Wed Aug 4 17:54:33 2010 @@ -185,6 +185,9 @@ Tests ----- +- Issue #9496: Provide a test suite for the rlcompleter module. Patch by + Michele Orr?. + - Issue #8605: Skip test_gdb if Python is compiled with optimizations. Documentation From python-checkins at python.org Wed Aug 4 18:45:21 2010 From: python-checkins at python.org (antoine.pitrou) Date: Wed, 4 Aug 2010 18:45:21 +0200 (CEST) Subject: [Python-checkins] r83726 - python/branches/py3k/Lib/test/test_ssl.py Message-ID: <20100804164521.D180AEE987@mail.python.org> Author: antoine.pitrou Date: Wed Aug 4 18:45:21 2010 New Revision: 83726 Log: In verbose mode, identify OpenSSL build and platform more precisely Modified: python/branches/py3k/Lib/test/test_ssl.py Modified: python/branches/py3k/Lib/test/test_ssl.py ============================================================================== --- python/branches/py3k/Lib/test/test_ssl.py (original) +++ python/branches/py3k/Lib/test/test_ssl.py Wed Aug 4 18:45:21 2010 @@ -15,6 +15,7 @@ import traceback import asyncore import weakref +import platform from http.server import HTTPServer, SimpleHTTPRequestHandler @@ -1424,6 +1425,23 @@ if skip_expected: raise unittest.SkipTest("No SSL support") + if support.verbose: + plats = { + 'Linux': platform.linux_distribution, + 'Mac': platform.mac_ver, + 'Windows': platform.win32_ver, + } + for name, func in plats.items(): + plat = func() + if plat and plat[0]: + plat = '%s %r' % (name, plat) + break + else: + plat = repr(platform.platform()) + print("test_ssl: testing with %r %r" % + (ssl.OPENSSL_VERSION, ssl.OPENSSL_VERSION_INFO)) + print(" under %s" % plat) + for filename in [ CERTFILE, SVN_PYTHON_ORG_ROOT_CERT, BYTES_CERTFILE, ONLYCERT, ONLYKEY, BYTES_ONLYCERT, BYTES_ONLYKEY, From python-checkins at python.org Wed Aug 4 19:14:10 2010 From: python-checkins at python.org (antoine.pitrou) Date: Wed, 4 Aug 2010 19:14:10 +0200 (CEST) Subject: [Python-checkins] r83727 - python/branches/py3k/Lib/test/test_ssl.py Message-ID: <20100804171410.2C587F5B5@mail.python.org> Author: antoine.pitrou Date: Wed Aug 4 19:14:06 2010 New Revision: 83727 Log: Try to fix issue #9415: skip some tests on broken Ubuntu OpenSSL Modified: python/branches/py3k/Lib/test/test_ssl.py Modified: python/branches/py3k/Lib/test/test_ssl.py ============================================================================== --- python/branches/py3k/Lib/test/test_ssl.py (original) +++ python/branches/py3k/Lib/test/test_ssl.py Wed Aug 4 19:14:06 2010 @@ -16,6 +16,7 @@ import asyncore import weakref import platform +import functools from http.server import HTTPServer, SimpleHTTPRequestHandler @@ -66,6 +67,20 @@ return ssl.OPENSSL_VERSION_INFO >= (0, 9, 7, 8, 15) +# Issue #9415: Ubuntu hijacks their OpenSSL and forcefully disables SSLv2 +def skip_if_broken_ubuntu_ssl(func): + @functools.wraps(func) + def f(*args, **kwargs): + try: + ssl.SSLContext(ssl.PROTOCOL_SSLv2) + except ssl.SSLError: + if (ssl.OPENSSL_VERSION_INFO == (0, 9, 8, 15, 15) and + platform.linux_distribution() == ('debian', 'squeeze/sid', '')): + raise unittest.SkipTest("Patched Ubuntu OpenSSL breaks behaviour") + return func(*args, **kwargs) + return f + + class BasicSocketTests(unittest.TestCase): def test_constants(self): @@ -176,6 +191,7 @@ class ContextTests(unittest.TestCase): + @skip_if_broken_ubuntu_ssl def test_constructor(self): ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv2) ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23) @@ -185,6 +201,7 @@ self.assertRaises(ValueError, ssl.SSLContext, -1) self.assertRaises(ValueError, ssl.SSLContext, 42) + @skip_if_broken_ubuntu_ssl def test_protocol(self): for proto in PROTOCOLS: ctx = ssl.SSLContext(proto) @@ -197,6 +214,7 @@ with self.assertRaisesRegexp(ssl.SSLError, "No cipher can be selected"): ctx.set_ciphers("^$:,;?*'dorothyx") + @skip_if_broken_ubuntu_ssl def test_options(self): ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1) # OP_ALL is the default value @@ -938,6 +956,7 @@ class ThreadedTests(unittest.TestCase): + @skip_if_broken_ubuntu_ssl def test_echo(self): """Basic test of an SSL client connecting to a server""" if support.verbose: @@ -1040,6 +1059,7 @@ finally: t.join() + @skip_if_broken_ubuntu_ssl def test_protocol_sslv2(self): """Connecting to an SSLv2 server with various client options""" if support.verbose: @@ -1060,6 +1080,7 @@ try_protocol_combo(ssl.PROTOCOL_SSLv2, ssl.PROTOCOL_SSLv23, True, client_options=ssl.OP_NO_TLSv1) + @skip_if_broken_ubuntu_ssl def test_protocol_sslv23(self): """Connecting to an SSLv23 server with various client options""" if support.verbose: @@ -1094,6 +1115,7 @@ server_options=ssl.OP_NO_TLSv1) + @skip_if_broken_ubuntu_ssl def test_protocol_sslv3(self): """Connecting to an SSLv3 server with various client options""" if support.verbose: @@ -1109,6 +1131,7 @@ try_protocol_combo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_SSLv23, True, client_options=ssl.OP_NO_SSLv2) + @skip_if_broken_ubuntu_ssl def test_protocol_tlsv1(self): """Connecting to a TLSv1 server with various client options""" if support.verbose: From python-checkins at python.org Wed Aug 4 19:38:33 2010 From: python-checkins at python.org (antoine.pitrou) Date: Wed, 4 Aug 2010 19:38:33 +0200 (CEST) Subject: [Python-checkins] r83728 - in python/branches/release27-maint: Lib/test/test_ssl.py Message-ID: <20100804173833.B06C9EDFE@mail.python.org> Author: antoine.pitrou Date: Wed Aug 4 19:38:33 2010 New Revision: 83728 Log: Merged revisions 83727 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83727 | antoine.pitrou | 2010-08-04 19:14:06 +0200 (mer., 04 ao?t 2010) | 3 lines Try to fix issue #9415: skip some tests on broken Ubuntu OpenSSL ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Lib/test/test_ssl.py Modified: python/branches/release27-maint/Lib/test/test_ssl.py ============================================================================== --- python/branches/release27-maint/Lib/test/test_ssl.py (original) +++ python/branches/release27-maint/Lib/test/test_ssl.py Wed Aug 4 19:38:33 2010 @@ -14,6 +14,8 @@ import urllib, urlparse import traceback import weakref +import functools +import platform from BaseHTTPServer import HTTPServer from SimpleHTTPServer import SimpleHTTPRequestHandler @@ -54,6 +56,28 @@ else: raise +# Issue #9415: Ubuntu hijacks their OpenSSL and forcefully disables SSLv2 +def skip_if_broken_ubuntu_ssl(func): + # We need to access the lower-level wrapper in order to create an + # implicit SSL context without trying to connect or listen. + import _ssl + @functools.wraps(func) + def f(*args, **kwargs): + try: + s = socket.socket(socket.AF_INET) + _ssl.sslwrap(s._sock, 0, None, None, + ssl.CERT_NONE, ssl.PROTOCOL_SSLv2, None, None) + except ssl.SSLError as e: + if (ssl.OPENSSL_VERSION_INFO == (0, 9, 8, 15, 15) and + platform.linux_distribution() == ('debian', 'squeeze/sid', '') + and 'Invalid SSL protocol variant specified' in str(e)): + raise unittest.SkipTest("Patched Ubuntu OpenSSL breaks behaviour") + return func(*args, **kwargs) + return f + + +class BasicSocketTests(unittest.TestCase): + def test_constants(self): ssl.PROTOCOL_SSLv2 ssl.PROTOCOL_SSLv23 @@ -807,6 +831,7 @@ finally: t.join() + @skip_if_broken_ubuntu_ssl def test_echo(self): """Basic test of an SSL client connecting to a server""" if test_support.verbose: @@ -872,6 +897,7 @@ bad_cert_test(os.path.join(os.path.dirname(__file__) or os.curdir, "badkey.pem")) + @skip_if_broken_ubuntu_ssl def test_protocol_sslv2(self): """Connecting to an SSLv2 server with various client options""" if test_support.verbose: @@ -883,6 +909,7 @@ try_protocol_combo(ssl.PROTOCOL_SSLv2, ssl.PROTOCOL_SSLv3, False) try_protocol_combo(ssl.PROTOCOL_SSLv2, ssl.PROTOCOL_TLSv1, False) + @skip_if_broken_ubuntu_ssl def test_protocol_sslv23(self): """Connecting to an SSLv23 server with various client options""" if test_support.verbose: @@ -907,6 +934,7 @@ try_protocol_combo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_SSLv23, True, ssl.CERT_REQUIRED) try_protocol_combo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_TLSv1, True, ssl.CERT_REQUIRED) + @skip_if_broken_ubuntu_ssl def test_protocol_sslv3(self): """Connecting to an SSLv3 server with various client options""" if test_support.verbose: @@ -918,6 +946,7 @@ try_protocol_combo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_SSLv23, False) try_protocol_combo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_TLSv1, False) + @skip_if_broken_ubuntu_ssl def test_protocol_tlsv1(self): """Connecting to a TLSv1 server with various client options""" if test_support.verbose: From python-checkins at python.org Wed Aug 4 19:46:23 2010 From: python-checkins at python.org (senthil.kumaran) Date: Wed, 4 Aug 2010 19:46:23 +0200 (CEST) Subject: [Python-checkins] r83729 - python/branches/py3k/Lib/urllib/request.py Message-ID: <20100804174623.5ADBCD39B@mail.python.org> Author: senthil.kumaran Date: Wed Aug 4 19:46:23 2010 New Revision: 83729 Log: Sending the auth info as string. Fix BytesWarning: str() on a bytes instance Exception on buildbot. Modified: python/branches/py3k/Lib/urllib/request.py Modified: python/branches/py3k/Lib/urllib/request.py ============================================================================== --- python/branches/py3k/Lib/urllib/request.py (original) +++ python/branches/py3k/Lib/urllib/request.py Wed Aug 4 19:46:23 2010 @@ -1591,13 +1591,13 @@ if proxy_passwd: import base64 - proxy_auth = base64.b64encode(proxy_passwd.encode()).strip() + proxy_auth = base64.b64encode(proxy_passwd.encode()).decode('ascii') else: proxy_auth = None if user_passwd: import base64 - auth = base64.b64encode(user_passwd.encode()).strip() + auth = base64.b64encode(user_passwd.encode()).decode('ascii') else: auth = None http_conn = connection_factory(host) From python-checkins at python.org Wed Aug 4 19:49:13 2010 From: python-checkins at python.org (senthil.kumaran) Date: Wed, 4 Aug 2010 19:49:13 +0200 (CEST) Subject: [Python-checkins] r83730 - in python/branches/release31-maint: Lib/urllib/request.py Message-ID: <20100804174913.81132EBF3@mail.python.org> Author: senthil.kumaran Date: Wed Aug 4 19:49:13 2010 New Revision: 83730 Log: Merged revisions 83729 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83729 | senthil.kumaran | 2010-08-04 23:16:23 +0530 (Wed, 04 Aug 2010) | 3 lines Sending the auth info as string. Fix BytesWarning: str() on a bytes instance Exception on buildbot. ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Lib/urllib/request.py Modified: python/branches/release31-maint/Lib/urllib/request.py ============================================================================== --- python/branches/release31-maint/Lib/urllib/request.py (original) +++ python/branches/release31-maint/Lib/urllib/request.py Wed Aug 4 19:49:13 2010 @@ -1595,13 +1595,13 @@ if proxy_passwd: import base64 - proxy_auth = base64.b64encode(proxy_passwd.encode()).strip() + proxy_auth = base64.b64encode(proxy_passwd.encode()).decode('ascii') else: proxy_auth = None if user_passwd: import base64 - auth = base64.b64encode(user_passwd.encode()).strip() + auth = base64.b64encode(user_passwd.encode()).decode('ascii') else: auth = None http_conn = connection_factory(host) From python-checkins at python.org Wed Aug 4 20:28:02 2010 From: python-checkins at python.org (antoine.pitrou) Date: Wed, 4 Aug 2010 20:28:02 +0200 (CEST) Subject: [Python-checkins] r83731 - in python/branches/py3k: Doc/library/functools.rst Lib/functools.py Lib/test/test_functools.py Misc/ACKS Misc/NEWS Message-ID: <20100804182802.DA764EE9A3@mail.python.org> Author: antoine.pitrou Date: Wed Aug 4 20:28:02 2010 New Revision: 83731 Log: Issue #8814: function annotations (the `__annotations__` attribute) are now included in the set of attributes copied by default by functools.wraps and functools.update_wrapper. Patch by Terrence Cole. Modified: python/branches/py3k/Doc/library/functools.rst python/branches/py3k/Lib/functools.py python/branches/py3k/Lib/test/test_functools.py python/branches/py3k/Misc/ACKS python/branches/py3k/Misc/NEWS Modified: python/branches/py3k/Doc/library/functools.rst ============================================================================== --- python/branches/py3k/Doc/library/functools.rst (original) +++ python/branches/py3k/Doc/library/functools.rst Wed Aug 4 20:28:02 2010 @@ -165,9 +165,9 @@ attributes of the wrapper function are updated with the corresponding attributes from the original function. The default values for these arguments are the module level constants *WRAPPER_ASSIGNMENTS* (which assigns to the wrapper - function's *__name__*, *__module__* and *__doc__*, the documentation string) and - *WRAPPER_UPDATES* (which updates the wrapper function's *__dict__*, i.e. the - instance dictionary). + function's *__name__*, *__module__*, *__annotations__* and *__doc__*, the + documentation string) and *WRAPPER_UPDATES* (which updates the wrapper + function's *__dict__*, i.e. the instance dictionary). The main intended use for this function is in :term:`decorator` functions which wrap the decorated function and return the wrapper. If the wrapper function is Modified: python/branches/py3k/Lib/functools.py ============================================================================== --- python/branches/py3k/Lib/functools.py (original) +++ python/branches/py3k/Lib/functools.py Wed Aug 4 20:28:02 2010 @@ -19,7 +19,7 @@ # update_wrapper() and wraps() are tools to help write # wrapper functions that can handle naive introspection -WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__doc__') +WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__doc__', '__annotations__') WRAPPER_UPDATES = ('__dict__',) def update_wrapper(wrapper, wrapped, @@ -37,7 +37,8 @@ function (defaults to functools.WRAPPER_UPDATES) """ for attr in assigned: - setattr(wrapper, attr, getattr(wrapped, attr)) + if hasattr(wrapped, attr): + setattr(wrapper, attr, getattr(wrapped, attr)) for attr in updated: getattr(wrapper, attr).update(getattr(wrapped, attr, {})) # Return the wrapper so this can be used as a decorator via partial() Modified: python/branches/py3k/Lib/test/test_functools.py ============================================================================== --- python/branches/py3k/Lib/test/test_functools.py (original) +++ python/branches/py3k/Lib/test/test_functools.py Wed Aug 4 20:28:02 2010 @@ -182,11 +182,11 @@ self.assertTrue(wrapped_attr[key] is wrapper_attr[key]) def _default_update(self): - def f(): + def f(a:'This is a new annotation'): """This is a test""" pass f.attr = 'This is also a test' - def wrapper(): + def wrapper(b:'This is the prior annotation'): pass functools.update_wrapper(wrapper, f) return wrapper, f @@ -196,6 +196,8 @@ self.check_wrapper(wrapper, f) self.assertEqual(wrapper.__name__, 'f') self.assertEqual(wrapper.attr, 'This is also a test') + self.assertEqual(wrapper.__annotations__['a'], 'This is a new annotation') + self.assertNotIn('b', wrapper.__annotations__) @unittest.skipIf(sys.flags.optimize >= 2, "Docstrings are omitted with -O2 and above") @@ -214,6 +216,7 @@ self.check_wrapper(wrapper, f, (), ()) self.assertEqual(wrapper.__name__, 'wrapper') self.assertEqual(wrapper.__doc__, None) + self.assertEqual(wrapper.__annotations__, {}) self.assertFalse(hasattr(wrapper, 'attr')) def test_selective_update(self): @@ -240,6 +243,7 @@ functools.update_wrapper(wrapper, max) self.assertEqual(wrapper.__name__, 'max') self.assertTrue(wrapper.__doc__.startswith('max(')) + self.assertEqual(wrapper.__annotations__, {}) class TestWraps(TestUpdateWrapper): Modified: python/branches/py3k/Misc/ACKS ============================================================================== --- python/branches/py3k/Misc/ACKS (original) +++ python/branches/py3k/Misc/ACKS Wed Aug 4 20:28:02 2010 @@ -154,6 +154,7 @@ Nick Coghlan Josh Cogliati Dave Cole +Terrence Cole Benjamin Collar Jeffery Collins Robert Collins Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Wed Aug 4 20:28:02 2010 @@ -37,6 +37,10 @@ Library ------- +- Issue #8814: function annotations (the ``__annotations__`` attribute) + are now included in the set of attributes copied by default by + functools.wraps and functools.update_wrapper. Patch by Terrence Cole. + - Issue #2944: asyncore doesn't handle connection refused correctly. - Issue #4184: Private attributes on smtpd.SMTPChannel made public and From python-checkins at python.org Wed Aug 4 20:42:43 2010 From: python-checkins at python.org (mark.dickinson) Date: Wed, 4 Aug 2010 20:42:43 +0200 (CEST) Subject: [Python-checkins] r83732 - python/branches/py3k/Doc/library/stdtypes.rst Message-ID: <20100804184243.C7681EFE5@mail.python.org> Author: mark.dickinson Date: Wed Aug 4 20:42:43 2010 New Revision: 83732 Log: Issue #9498: Add reference to sys.float_info from 'numeric types' docs. Thanks Yitz Gale. Modified: python/branches/py3k/Doc/library/stdtypes.rst Modified: python/branches/py3k/Doc/library/stdtypes.rst ============================================================================== --- python/branches/py3k/Doc/library/stdtypes.rst (original) +++ python/branches/py3k/Doc/library/stdtypes.rst Wed Aug 4 20:42:43 2010 @@ -217,14 +217,15 @@ There are three distinct numeric types: :dfn:`integers`, :dfn:`floating point numbers`, and :dfn:`complex numbers`. In addition, Booleans are a subtype of integers. Integers have unlimited precision. Floating point -numbers are implemented using :ctype:`double` in C---all bets on their -precision are off unless you happen to know the machine you are working -with. Complex numbers have a real and imaginary part, which are each -implemented using :ctype:`double` in C. To extract these parts from a -complex number *z*, use ``z.real`` and ``z.imag``. (The standard library -includes additional numeric types, :mod:`fractions` that hold rationals, -and :mod:`decimal` that hold floating-point numbers with user-definable -precision.) +numbers are usually implemented using :ctype:`double` in C; information +about the precision and internal representation of floating point +numbers for the machine on which your program is running is available +in :data:`sys.float_info`. Complex numbers have a real and imaginary +part, which are each a floating point number. To extract these parts +from a complex number *z*, use ``z.real`` and ``z.imag``. (The standard +library includes additional numeric types, :mod:`fractions` that hold +rationals, and :mod:`decimal` that hold floating-point numbers with +user-definable precision.) .. index:: pair: numeric; literals From python-checkins at python.org Wed Aug 4 20:43:36 2010 From: python-checkins at python.org (mark.dickinson) Date: Wed, 4 Aug 2010 20:43:36 +0200 (CEST) Subject: [Python-checkins] r83733 - in python/branches/release31-maint: Doc/library/stdtypes.rst Message-ID: <20100804184336.3DC2AEFE5@mail.python.org> Author: mark.dickinson Date: Wed Aug 4 20:43:36 2010 New Revision: 83733 Log: Merged revisions 83732 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83732 | mark.dickinson | 2010-08-04 19:42:43 +0100 (Wed, 04 Aug 2010) | 3 lines Issue #9498: Add reference to sys.float_info from 'numeric types' docs. Thanks Yitz Gale. ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Doc/library/stdtypes.rst Modified: python/branches/release31-maint/Doc/library/stdtypes.rst ============================================================================== --- python/branches/release31-maint/Doc/library/stdtypes.rst (original) +++ python/branches/release31-maint/Doc/library/stdtypes.rst Wed Aug 4 20:43:36 2010 @@ -216,14 +216,15 @@ There are three distinct numeric types: :dfn:`integers`, :dfn:`floating point numbers`, and :dfn:`complex numbers`. In addition, Booleans are a subtype of integers. Integers have unlimited precision. Floating point -numbers are implemented using :ctype:`double` in C---all bets on their -precision are off unless you happen to know the machine you are working -with. Complex numbers have a real and imaginary part, which are each -implemented using :ctype:`double` in C. To extract these parts from a -complex number *z*, use ``z.real`` and ``z.imag``. (The standard library -includes additional numeric types, :mod:`fractions` that hold rationals, -and :mod:`decimal` that hold floating-point numbers with user-definable -precision.) +numbers are usually implemented using :ctype:`double` in C; information +about the precision and internal representation of floating point +numbers for the machine on which your program is running is available +in :data:`sys.float_info`. Complex numbers have a real and imaginary +part, which are each a floating point number. To extract these parts +from a complex number *z*, use ``z.real`` and ``z.imag``. (The standard +library includes additional numeric types, :mod:`fractions` that hold +rationals, and :mod:`decimal` that hold floating-point numbers with +user-definable precision.) .. index:: pair: numeric; literals From python-checkins at python.org Wed Aug 4 20:48:34 2010 From: python-checkins at python.org (mark.dickinson) Date: Wed, 4 Aug 2010 20:48:34 +0200 (CEST) Subject: [Python-checkins] r83734 - in python/branches/release27-maint: Doc/library/stdtypes.rst Message-ID: <20100804184834.EC52DEDFE@mail.python.org> Author: mark.dickinson Date: Wed Aug 4 20:48:34 2010 New Revision: 83734 Log: Merged revisions 83732 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83732 | mark.dickinson | 2010-08-04 19:42:43 +0100 (Wed, 04 Aug 2010) | 3 lines Issue #9498: Add reference to sys.float_info from 'numeric types' docs. Thanks Yitz Gale. ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Doc/library/stdtypes.rst Modified: python/branches/release27-maint/Doc/library/stdtypes.rst ============================================================================== --- python/branches/release27-maint/Doc/library/stdtypes.rst (original) +++ python/branches/release27-maint/Doc/library/stdtypes.rst Wed Aug 4 20:48:34 2010 @@ -224,18 +224,20 @@ pair: C; language There are four distinct numeric types: :dfn:`plain integers`, :dfn:`long -integers`, :dfn:`floating point numbers`, and :dfn:`complex numbers`. In +integers`, :dfn:`floating point numbers`, and :dfn:`complex numbers`. In addition, Booleans are a subtype of plain integers. Plain integers (also just called :dfn:`integers`) are implemented using :ctype:`long` in C, which gives them at least 32 bits of precision (``sys.maxint`` is always set to the maximum plain integer value for the current platform, the minimum value is -``-sys.maxint - 1``). Long integers have unlimited precision. Floating point -numbers are implemented using :ctype:`double` in C. All bets on their precision -are off unless you happen to know the machine you are working with. - -Complex numbers have a real and imaginary part, which are each implemented using -:ctype:`double` in C. To extract these parts from a complex number *z*, use -``z.real`` and ``z.imag``. +``-sys.maxint - 1``). Long integers have unlimited precision. Floating point +numbers are usually implemented using :ctype:`double` in C; information about +the precision and internal representation of floating point numbers for the +machine on which your program is running is available in +:data:`sys.float_info`. Complex numbers have a real and imaginary part, which +are each a floating point number. To extract these parts from a complex number +*z*, use ``z.real`` and ``z.imag``. (The standard library includes additional +numeric types, :mod:`fractions` that hold rationals, and :mod:`decimal` that +hold floating-point numbers with user-definable precision.) .. index:: pair: numeric; literals From python-checkins at python.org Wed Aug 4 21:08:21 2010 From: python-checkins at python.org (martin.v.loewis) Date: Wed, 4 Aug 2010 21:08:21 +0200 (CEST) Subject: [Python-checkins] r83735 - python/branches/py3k/Misc/developers.txt Message-ID: <20100804190821.19613D36E@mail.python.org> Author: martin.v.loewis Date: Wed Aug 4 21:08:20 2010 New Revision: 83735 Log: Add Terry Reedy. Modified: python/branches/py3k/Misc/developers.txt Modified: python/branches/py3k/Misc/developers.txt ============================================================================== --- python/branches/py3k/Misc/developers.txt (original) +++ python/branches/py3k/Misc/developers.txt Wed Aug 4 21:08:20 2010 @@ -20,6 +20,9 @@ Permissions History ------------------- +- Terry Reedy was given commit access on Aug 04 2010 by MvL, + at suggestion of Nick Coghlan. + - Brian Quinlan was given commit access on Jul 26 2010 by GFB, for work related to PEP 3148. From python-checkins at python.org Wed Aug 4 22:56:28 2010 From: python-checkins at python.org (mark.dickinson) Date: Wed, 4 Aug 2010 22:56:28 +0200 (CEST) Subject: [Python-checkins] r83736 - in python/branches/py3k: Doc/tutorial/floatingpoint.rst Include/floatobject.h Lib/test/formatfloat_testcases.txt Lib/test/test_float.py Lib/test/test_tokenize.py Lib/test/test_unicodedata.py Misc/NEWS Objects/complexobject.c Objects/floatobject.c Objects/stringlib/formatter.h Message-ID: <20100804205628.84AEEEE9B3@mail.python.org> Author: mark.dickinson Date: Wed Aug 4 22:56:28 2010 New Revision: 83736 Log: Issue #9337: Make float.__str__ identical to float.__repr__. (And similarly for complex numbers.) Modified: python/branches/py3k/Doc/tutorial/floatingpoint.rst python/branches/py3k/Include/floatobject.h python/branches/py3k/Lib/test/formatfloat_testcases.txt python/branches/py3k/Lib/test/test_float.py python/branches/py3k/Lib/test/test_tokenize.py python/branches/py3k/Lib/test/test_unicodedata.py python/branches/py3k/Misc/NEWS python/branches/py3k/Objects/complexobject.c python/branches/py3k/Objects/floatobject.c python/branches/py3k/Objects/stringlib/formatter.h Modified: python/branches/py3k/Doc/tutorial/floatingpoint.rst ============================================================================== --- python/branches/py3k/Doc/tutorial/floatingpoint.rst (original) +++ python/branches/py3k/Doc/tutorial/floatingpoint.rst Wed Aug 4 22:56:28 2010 @@ -92,18 +92,17 @@ (although some languages may not *display* the difference by default, or in all output modes). -Python's built-in :func:`str` function produces only 12 significant digits, and -you may wish to use that instead. It's unusual for ``eval(str(x))`` to -reproduce *x*, but the output may be more pleasant to look at:: +For more pleasant output, you may may wish to use string formatting to produce a limited number of significant digits:: - >>> str(math.pi) + >>> format(math.pi, '.12g') # give 12 significant digits '3.14159265359' + >>> format(math.pi, '.2f') # give 2 digits after the point + '3.14' + >>> repr(math.pi) '3.141592653589793' - >>> format(math.pi, '.2f') - '3.14' It's important to realize that this is, in a real sense, an illusion: you're simply rounding the *display* of the true machine value. Modified: python/branches/py3k/Include/floatobject.h ============================================================================== --- python/branches/py3k/Include/floatobject.h (original) +++ python/branches/py3k/Include/floatobject.h Wed Aug 4 22:56:28 2010 @@ -21,12 +21,6 @@ #define PyFloat_Check(op) PyObject_TypeCheck(op, &PyFloat_Type) #define PyFloat_CheckExact(op) (Py_TYPE(op) == &PyFloat_Type) -/* The str() precision PyFloat_STR_PRECISION is chosen so that in most cases, - the rounding noise created by various operations is suppressed, while - giving plenty of precision for practical use. */ - -#define PyFloat_STR_PRECISION 12 - #ifdef Py_NAN #define Py_RETURN_NAN return PyFloat_FromDouble(Py_NAN) #endif Modified: python/branches/py3k/Lib/test/formatfloat_testcases.txt ============================================================================== --- python/branches/py3k/Lib/test/formatfloat_testcases.txt (original) +++ python/branches/py3k/Lib/test/formatfloat_testcases.txt Wed Aug 4 22:56:28 2010 @@ -314,43 +314,37 @@ %#.5g 234.56 -> 234.56 %#.6g 234.56 -> 234.560 --- for repr formatting see the separate test_short_repr test in --- test_float.py. Not all platforms use short repr for floats. - --- str formatting. Result always includes decimal point and at +-- repr formatting. Result always includes decimal point and at -- least one digit after the point, or an exponent. -%s 0 -> 0.0 -%s 1 -> 1.0 - -%s 0.01 -> 0.01 -%s 0.02 -> 0.02 -%s 0.03 -> 0.03 -%s 0.04 -> 0.04 -%s 0.05 -> 0.05 - --- str truncates to 12 significant digits -%s 1.234123412341 -> 1.23412341234 -%s 1.23412341234 -> 1.23412341234 -%s 1.2341234123 -> 1.2341234123 +%r 0 -> 0.0 +%r 1 -> 1.0 --- values >= 1e11 get an exponent -%s 10 -> 10.0 -%s 100 -> 100.0 -%s 1e10 -> 10000000000.0 -%s 9.999e10 -> 99990000000.0 -%s 99999999999 -> 99999999999.0 -%s 99999999999.9 -> 99999999999.9 -%s 99999999999.99 -> 1e+11 -%s 1e11 -> 1e+11 -%s 1e12 -> 1e+12 +%r 0.01 -> 0.01 +%r 0.02 -> 0.02 +%r 0.03 -> 0.03 +%r 0.04 -> 0.04 +%r 0.05 -> 0.05 + +-- values >= 1e16 get an exponent +%r 10 -> 10.0 +%r 100 -> 100.0 +%r 1e15 -> 1000000000000000.0 +%r 9.999e15 -> 9999000000000000.0 +%r 9999999999999998 -> 9999999999999998.0 +%r 9999999999999999 -> 1e+16 +%r 1e16 -> 1e+16 +%r 1e17 -> 1e+17 -- as do values < 1e-4 -%s 1e-3 -> 0.001 -%s 1.001e-4 -> 0.0001001 -%s 1.000000000001e-4 -> 0.0001 -%s 1.00000000001e-4 -> 0.000100000000001 -%s 1.0000000001e-4 -> 0.00010000000001 -%s 1e-4 -> 0.0001 -%s 0.999999999999e-4 -> 9.99999999999e-05 -%s 0.999e-4 -> 9.99e-05 -%s 1e-5 -> 1e-05 +%r 1e-3 -> 0.001 +%r 1.001e-4 -> 0.0001001 +%r 1.0000000000000001e-4 -> 0.0001 +%r 1.000000000000001e-4 -> 0.0001000000000000001 +%r 1.00000000001e-4 -> 0.000100000000001 +%r 1.0000000001e-4 -> 0.00010000000001 +%r 1e-4 -> 0.0001 +%r 0.99999999999999999e-4 -> 0.0001 +%r 0.9999999999999999e-4 -> 9.999999999999999e-05 +%r 0.999999999999e-4 -> 9.99999999999e-05 +%r 0.999e-4 -> 9.99e-05 +%r 1e-5 -> 1e-05 Modified: python/branches/py3k/Lib/test/test_float.py ============================================================================== --- python/branches/py3k/Lib/test/test_float.py (original) +++ python/branches/py3k/Lib/test/test_float.py Wed Aug 4 22:56:28 2010 @@ -617,7 +617,9 @@ negs = '-'+s self.assertEqual(s, repr(float(s))) self.assertEqual(negs, repr(float(negs))) - + # Since Python 3.2, repr and str are identical + self.assertEqual(repr(float(s)), str(float(s))) + self.assertEqual(repr(float(negs)), str(float(negs))) @requires_IEEE_754 class RoundTestCase(unittest.TestCase): Modified: python/branches/py3k/Lib/test/test_tokenize.py ============================================================================== --- python/branches/py3k/Lib/test/test_tokenize.py (original) +++ python/branches/py3k/Lib/test/test_tokenize.py Wed Aug 4 22:56:28 2010 @@ -598,11 +598,11 @@ The format of the exponent is inherited from the platform C library. Known cases are "e-007" (Windows) and "e-07" (not Windows). Since - we're only showing 12 digits, and the 13th isn't close to 5, the + we're only showing 11 digits, and the 12th isn't close to 5, the rest of the output should be platform-independent. >>> exec(s) #doctest: +ELLIPSIS - -3.21716034272e-0...7 + -3.2171603427...e-0...7 Output from calculations with Decimal should be identical across all platforms. Modified: python/branches/py3k/Lib/test/test_unicodedata.py ============================================================================== --- python/branches/py3k/Lib/test/test_unicodedata.py (original) +++ python/branches/py3k/Lib/test/test_unicodedata.py Wed Aug 4 22:56:28 2010 @@ -80,8 +80,7 @@ class UnicodeFunctionsTest(UnicodeDatabaseTest): # update this, if the database changes - expectedchecksum = '6ccf1b1a36460d2694f9b0b0f0324942fe70ede6' - + expectedchecksum = 'e89a6380093a00a7685ac7b92e7367d737fcb79b' def test_function_checksum(self): data = [] h = hashlib.sha1() @@ -90,9 +89,9 @@ char = chr(i) data = [ # Properties - str(self.db.digit(char, -1)), - str(self.db.numeric(char, -1)), - str(self.db.decimal(char, -1)), + format(self.db.digit(char, -1), '.12g'), + format(self.db.numeric(char, -1), '.12g'), + format(self.db.decimal(char, -1), '.12g'), self.db.category(char), self.db.bidirectional(char), self.db.decomposition(char), Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Wed Aug 4 22:56:28 2010 @@ -12,6 +12,9 @@ Core and Builtins ----------------- +- Issue #9337: The str() of a float or complex number is now identical + to its repr(). + - Issue #9416: Fix some issues with complex formatting where the output with no type specifier failed to match the str output: Modified: python/branches/py3k/Objects/complexobject.c ============================================================================== --- python/branches/py3k/Objects/complexobject.c (original) +++ python/branches/py3k/Objects/complexobject.c Wed Aug 4 22:56:28 2010 @@ -394,12 +394,6 @@ return complex_format(v, 0, 'r'); } -static PyObject * -complex_str(PyComplexObject *v) -{ - return complex_format(v, PyFloat_STR_PRECISION, 'g'); -} - static long complex_hash(PyComplexObject *v) { @@ -1104,7 +1098,7 @@ 0, /* tp_as_mapping */ (hashfunc)complex_hash, /* tp_hash */ 0, /* tp_call */ - (reprfunc)complex_str, /* tp_str */ + (reprfunc)complex_repr, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Modified: python/branches/py3k/Objects/floatobject.c ============================================================================== --- python/branches/py3k/Objects/floatobject.c (original) +++ python/branches/py3k/Objects/floatobject.c Wed Aug 4 22:56:28 2010 @@ -305,32 +305,20 @@ } static PyObject * -float_str_or_repr(PyFloatObject *v, int precision, char format_code) +float_repr(PyFloatObject *v) { PyObject *result; char *buf = PyOS_double_to_string(PyFloat_AS_DOUBLE(v), - format_code, precision, + 'r', 0, Py_DTSF_ADD_DOT_0, NULL); if (!buf) - return PyErr_NoMemory(); + return PyErr_NoMemory(); result = PyUnicode_FromString(buf); PyMem_Free(buf); return result; } -static PyObject * -float_repr(PyFloatObject *v) -{ - return float_str_or_repr(v, 0, 'r'); -} - -static PyObject * -float_str(PyFloatObject *v) -{ - return float_str_or_repr(v, PyFloat_STR_PRECISION, 'g'); -} - /* Comparison is pretty much a nightmare. When comparing float to float, * we do it as straightforwardly (and long-windedly) as conceivable, so * that, e.g., Python x == y delivers the same result as the platform @@ -1169,7 +1157,7 @@ CONVERT_TO_DOUBLE(v, x); if (Py_IS_NAN(x) || Py_IS_INFINITY(x)) - return float_str((PyFloatObject *)v); + return float_repr((PyFloatObject *)v); if (x == 0.0) { if (copysign(1.0, x) == -1.0) @@ -1873,7 +1861,7 @@ 0, /* tp_as_mapping */ (hashfunc)float_hash, /* tp_hash */ 0, /* tp_call */ - (reprfunc)float_str, /* tp_str */ + (reprfunc)float_repr, /* tp_str */ PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ Modified: python/branches/py3k/Objects/stringlib/formatter.h ============================================================================== --- python/branches/py3k/Objects/stringlib/formatter.h (original) +++ python/branches/py3k/Objects/stringlib/formatter.h Wed Aug 4 22:56:28 2010 @@ -950,11 +950,12 @@ } if (type == '\0') { - /* Omitted type specifier. This is like 'g' but with at least one - digit after the decimal point, and different default precision.*/ - type = 'g'; - default_precision = PyFloat_STR_PRECISION; + /* Omitted type specifier. Behaves in the same way as repr(x) + and str(x) if no precision is given, else like 'g', but with + at least one digit after the decimal point. */ flags |= Py_DTSF_ADD_DOT_0; + type = 'r'; + default_precision = 0; } if (type == 'n') @@ -974,6 +975,8 @@ if (precision < 0) precision = default_precision; + else if (type == 'r') + type = 'g'; /* Cast "type", because if we're in unicode we need to pass a 8-bit char. This is safe, because we've restricted what "type" @@ -1134,8 +1137,8 @@ if (type == '\0') { /* Omitted type specifier. Should be like str(self). */ - type = 'g'; - default_precision = PyFloat_STR_PRECISION; + type = 'r'; + default_precision = 0; if (re == 0.0 && copysign(1.0, re) == 1.0) skip_re = 1; else @@ -1149,6 +1152,8 @@ if (precision < 0) precision = default_precision; + else if (type == 'r') + type = 'g'; /* Cast "type", because if we're in unicode we need to pass a 8-bit char. This is safe, because we've restricted what "type" From python-checkins at python.org Wed Aug 4 23:44:47 2010 From: python-checkins at python.org (mark.dickinson) Date: Wed, 4 Aug 2010 23:44:47 +0200 (CEST) Subject: [Python-checkins] r83737 - python/branches/release27-maint/Doc/tutorial/floatingpoint.rst Message-ID: <20100804214447.397DDC311@mail.python.org> Author: mark.dickinson Date: Wed Aug 4 23:44:47 2010 New Revision: 83737 Log: More tweaks to floating-point section of the tutorial. Modified: python/branches/release27-maint/Doc/tutorial/floatingpoint.rst Modified: python/branches/release27-maint/Doc/tutorial/floatingpoint.rst ============================================================================== --- python/branches/release27-maint/Doc/tutorial/floatingpoint.rst (original) +++ python/branches/release27-maint/Doc/tutorial/floatingpoint.rst Wed Aug 4 23:44:47 2010 @@ -48,9 +48,11 @@ 0.0001100110011001100110011001100110011001100110011... -Stop at any finite number of bits, and you get an approximation. On a typical -machine, there are 53 bits of precision available, so the value stored -internally is the binary fraction :: +Stop at any finite number of bits, and you get an approximation. + +On a typical machine running Python, there are 53 bits of precision available +for a Python float, so the value stored internally when you enter the decimal +number ``0.1`` is the binary fraction :: 0.00011001100110011001100110011001100110011001100110011010 @@ -80,14 +82,14 @@ >>> 0.1 + 0.2 0.30000000000000004 -Note that this is in the very nature of binary floating-point: this is not a bug -in Python, and it is not a bug in your code either. You'll see the same kind of -thing in all languages that support your hardware's floating-point arithmetic -(although some languages may not *display* the difference by default, or in all -output modes). +Note that this is in the very nature of binary floating-point: this is not a +bug in Python, and it is not a bug in your code either. You'll see the same +kind of thing in all languages that support your hardware's floating-point +arithmetic (although some languages may not *display* the difference by +default, or in all output modes). -Other surprises follow from this one. For example, if you try to round the value -2.675 to two decimal places, you get this :: +Other surprises follow from this one. For example, if you try to round the +value 2.675 to two decimal places, you get this :: >>> round(2.675, 2) 2.67 @@ -96,7 +98,7 @@ to the nearest value, rounding ties away from zero. Since the decimal fraction 2.675 is exactly halfway between 2.67 and 2.68, you might expect the result here to be (a binary approximation to) 2.68. It's not, because when the -decimal literal ``2.675`` is converted to a binary floating-point number, it's +decimal string ``2.675`` is converted to a binary floating-point number, it's again replaced with a binary approximation, whose exact value is :: 2.67499999999999982236431605997495353221893310546875 @@ -113,8 +115,8 @@ >>> Decimal(2.675) Decimal('2.67499999999999982236431605997495353221893310546875') -Another consequence is that since 0.1 is not exactly 1/10, summing ten values of -0.1 may not yield exactly 1.0, either:: +Another consequence is that since 0.1 is not exactly 1/10, summing ten values +of 0.1 may not yield exactly 1.0, either:: >>> sum = 0.0 >>> for i in range(10): @@ -137,9 +139,9 @@ While pathological cases do exist, for most casual use of floating-point arithmetic you'll see the result you expect in the end if you simply round the -display of your final results to the number of decimal digits you expect. -:func:`str` usually suffices, and for finer control see the :meth:`str.format` -method's format specifiers in :ref:`formatstrings`. +display of your final results to the number of decimal digits you expect. For +fine control over how a float is displayed see the :meth:`str.format` method's +format specifiers in :ref:`formatstrings`. .. _tut-fp-error: @@ -147,9 +149,9 @@ Representation Error ==================== -This section explains the "0.1" example in detail, and shows how you can perform -an exact analysis of cases like this yourself. Basic familiarity with binary -floating-point representation is assumed. +This section explains the "0.1" example in detail, and shows how you can +perform an exact analysis of cases like this yourself. Basic familiarity with +binary floating-point representation is assumed. :dfn:`Representation error` refers to the fact that some (most, actually) decimal fractions cannot be represented exactly as binary (base 2) fractions. @@ -176,24 +178,24 @@ the best value for *N* is 56:: >>> 2**52 - 4503599627370496L + 4503599627370496 >>> 2**53 - 9007199254740992L + 9007199254740992 >>> 2**56/10 - 7205759403792793L + 7205759403792793 -That is, 56 is the only value for *N* that leaves *J* with exactly 53 bits. The -best possible value for *J* is then that quotient rounded:: +That is, 56 is the only value for *N* that leaves *J* with exactly 53 bits. +The best possible value for *J* is then that quotient rounded:: >>> q, r = divmod(2**56, 10) >>> r - 6L + 6 Since the remainder is more than half of 10, the best approximation is obtained by rounding up:: >>> q+1 - 7205759403792794L + 7205759403792794 Therefore the best possible approximation to 1/10 in 754 double precision is that over 2\*\*56, or :: @@ -201,8 +203,8 @@ 7205759403792794 / 72057594037927936 Note that since we rounded up, this is actually a little bit larger than 1/10; -if we had not rounded up, the quotient would have been a little bit smaller than -1/10. But in no case can it be *exactly* 1/10! +if we had not rounded up, the quotient would have been a little bit smaller +than 1/10. But in no case can it be *exactly* 1/10! So the computer never "sees" 1/10: what it sees is the exact fraction given above, the best 754 double approximation it can get:: @@ -213,12 +215,12 @@ If we multiply that fraction by 10\*\*30, we can see the (truncated) value of its 30 most significant decimal digits:: - >>> 7205759403792794 * 10**30 / 2**56 + >>> 7205759403792794 * 10**30 // 2**56 100000000000000005551115123125L meaning that the exact number stored in the computer is approximately equal to the decimal value 0.100000000000000005551115123125. In versions prior to Python 2.7 and Python 3.1, Python rounded this value to 17 significant digits, -giving '0.10000000000000001'. In current versions, Python displays a value based -on the shortest decimal fraction that rounds correctly back to the true binary -value, resulting simply in '0.1'. +giving '0.10000000000000001'. In current versions, Python displays a value +based on the shortest decimal fraction that rounds correctly back to the true +binary value, resulting simply in '0.1'. From python-checkins at python.org Thu Aug 5 00:30:09 2010 From: python-checkins at python.org (barry.warsaw) Date: Thu, 5 Aug 2010 00:30:09 +0200 (CEST) Subject: [Python-checkins] r83738 - python/branches/release26-maint/Include/patchlevel.h Message-ID: <20100804223009.8F49EEE9CA@mail.python.org> Author: barry.warsaw Date: Thu Aug 5 00:30:09 2010 New Revision: 83738 Log: Post 2.6.6rc1 Modified: python/branches/release26-maint/Include/patchlevel.h Modified: python/branches/release26-maint/Include/patchlevel.h ============================================================================== --- python/branches/release26-maint/Include/patchlevel.h (original) +++ python/branches/release26-maint/Include/patchlevel.h Thu Aug 5 00:30:09 2010 @@ -27,7 +27,7 @@ #define PY_RELEASE_SERIAL 1 /* Version as a string */ -#define PY_VERSION "2.6.6rc1" +#define PY_VERSION "2.6.6rc1+" /*--end constants--*/ /* Subversion Revision number of this file (not of the repository) */ From python-checkins at python.org Thu Aug 5 03:30:23 2010 From: python-checkins at python.org (antoine.pitrou) Date: Thu, 5 Aug 2010 03:30:23 +0200 (CEST) Subject: [Python-checkins] r83739 - python/branches/release27-maint/Lib/test/test_ssl.py Message-ID: <20100805013023.895AAF067@mail.python.org> Author: antoine.pitrou Date: Thu Aug 5 03:30:23 2010 New Revision: 83739 Log: Don't crash when the _ssl module isn't built Modified: python/branches/release27-maint/Lib/test/test_ssl.py Modified: python/branches/release27-maint/Lib/test/test_ssl.py ============================================================================== --- python/branches/release27-maint/Lib/test/test_ssl.py (original) +++ python/branches/release27-maint/Lib/test/test_ssl.py Thu Aug 5 03:30:23 2010 @@ -60,7 +60,11 @@ def skip_if_broken_ubuntu_ssl(func): # We need to access the lower-level wrapper in order to create an # implicit SSL context without trying to connect or listen. - import _ssl + try: + import _ssl + except ImportError: + # The returned function won't get executed, just ignore the error + pass @functools.wraps(func) def f(*args, **kwargs): try: From solipsis at pitrou.net Thu Aug 5 05:01:17 2010 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Thu, 05 Aug 2010 05:01:17 +0200 Subject: [Python-checkins] Daily py3k reference leaks (r83736): sum=0 Message-ID: py3k results for svn r83736 (hg cset 0d51e81f0f5c) -------------------------------------------------- Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/py3k/refleaks/reflogRXuK4_', '-x'] From python-checkins at python.org Thu Aug 5 08:58:36 2010 From: python-checkins at python.org (alexandre.vassalotti) Date: Thu, 5 Aug 2010 08:58:36 +0200 (CEST) Subject: [Python-checkins] r83740 - in sandbox/trunk/2to3: README lib2to3/fixes/fix_operator.py lib2to3/tests/test_fixers.py Message-ID: <20100805065836.D63E2EE995@mail.python.org> Author: alexandre.vassalotti Date: Thu Aug 5 08:58:36 2010 New Revision: 83740 Log: Issue 5077: Update fixer for the other functions gone from the operator module. Patch by Meador Inge. Modified: sandbox/trunk/2to3/README sandbox/trunk/2to3/lib2to3/fixes/fix_operator.py sandbox/trunk/2to3/lib2to3/tests/test_fixers.py Modified: sandbox/trunk/2to3/README ============================================================================== --- sandbox/trunk/2to3/README (original) +++ sandbox/trunk/2to3/README Thu Aug 5 08:58:36 2010 @@ -90,8 +90,7 @@ * **fix_paren** - Add parentheses to places where they are needed in list comprehensions and generator expressions. -* **fix_operator** - "operator.isCallable(x)" -> "hasattr(x, '__call__')", - "operator.sequenceIncludes(x, y)" -> "operator.contains(a, b)" +* **fix_operator** - fixer for functions gone from the operator module. * **fix_print** - convert "print" statements to print() function calls. Modified: sandbox/trunk/2to3/lib2to3/fixes/fix_operator.py ============================================================================== --- sandbox/trunk/2to3/lib2to3/fixes/fix_operator.py (original) +++ sandbox/trunk/2to3/lib2to3/fixes/fix_operator.py Thu Aug 5 08:58:36 2010 @@ -1,40 +1,87 @@ -"""Fixer for operator.{isCallable,sequenceIncludes} +"""Fixer for operator functions. -operator.isCallable(obj) -> hasattr(obj, '__call__') +operator.isCallable(obj) -> hasattr(obj, '__call__') operator.sequenceIncludes(obj) -> operator.contains(obj) +operator.isSequenceType(obj) -> isinstance(obj, collections.Sequence) +operator.isMappingType(obj) -> isinstance(obj, collections.Mapping) +operator.isNumberType(obj) -> isinstance(obj, numbers.Number) +operator.repeat(obj, n) -> operator.mul(obj, n) +operator.irepeat(obj, n) -> operator.imul(obj, n) """ # Local imports from .. import fixer_base -from ..fixer_util import Call, Name, String +from ..fixer_util import Call, Name, String, touch_import class FixOperator(fixer_base.BaseFix): - methods = "method=('isCallable'|'sequenceIncludes')" - func = "'(' func=any ')'" + methods = """ + method=('isCallable'|'sequenceIncludes' + |'isSequenceType'|'isMappingType'|'isNumberType' + |'repeat'|'irepeat') + """ + obj = "'(' obj=any ')'" PATTERN = """ power< module='operator' - trailer< '.' %(methods)s > trailer< %(func)s > > + trailer< '.' %(methods)s > trailer< %(obj)s > > | - power< %(methods)s trailer< %(func)s > > - """ % dict(methods=methods, func=func) + power< %(methods)s trailer< %(obj)s > > + """ % dict(methods=methods, obj=obj) def transform(self, node, results): + if self._check_method(node, results): + return self._get_method(results)(node, results) + + def _sequenceIncludes(self, node, results): + """operator.contains(%s)""" + return self._handle_rename(node, results, u"contains") + + def _isCallable(self, node, results): + """hasattr(%s, '__call__')""" + obj = results["obj"] + args = [obj.clone(), String(u", "), String(u"'__call__'")] + return Call(Name(u"hasattr"), args, prefix=node.prefix) + + def _repeat(self, node, results): + """operator.mul(%s)""" + return self._handle_rename(node, results, u"mul") + + def _irepeat(self, node, results): + """operator.imul(%s)""" + return self._handle_rename(node, results, u"imul") + + def _isSequenceType(self, node, results): + """isinstance(%s, collections.Sequence)""" + return self._handle_type2abc(node, results, "collections", "Sequence") + + def _isMappingType(self, node, results): + """isinstance(%s, collections.Mapping)""" + return self._handle_type2abc(node, results, "collections", "Mapping") + + def _isNumberType(self, node, results): + """isinstance(%s, numbers.Number)""" + return self._handle_type2abc(node, results, "numbers", "Number") + + def _handle_rename(self, node, results, name): method = results["method"][0] + method.value = name + method.changed() - if method.value == u"sequenceIncludes": - if "module" not in results: - # operator may not be in scope, so we can't make a change. - self.warning(node, "You should use operator.contains here.") - else: - method.value = u"contains" - method.changed() - elif method.value == u"isCallable": - if "module" not in results: - self.warning(node, - "You should use hasattr(%s, '__call__') here." % - results["func"].value) + def _handle_type2abc(self, node, results, module, abc): + touch_import(None, module, node) + obj = results['obj'] + args = [obj.clone(), String(', ' + ".".join([module, abc]))] + return Call(Name('isinstance'), args, prefix=node.get_prefix()) + + def _get_method(self, results): + return getattr(self, "_" + results["method"][0].value) + + def _check_method(self, node, results): + method = self._get_method(results) + if callable(method): + if "module" in results: + return True else: - func = results["func"] - args = [func.clone(), String(u", "), String(u"'__call__'")] - return Call(Name(u"hasattr"), args, prefix=node.prefix) + invocation_str = method.__doc__ % str(results["obj"]) + self.warning(node, "You should use '%s' here." % invocation_str) + return False Modified: sandbox/trunk/2to3/lib2to3/tests/test_fixers.py ============================================================================== --- sandbox/trunk/2to3/lib2to3/tests/test_fixers.py (original) +++ sandbox/trunk/2to3/lib2to3/tests/test_fixers.py Thu Aug 5 08:58:36 2010 @@ -4307,13 +4307,89 @@ a = "operator.contains(x, y)" self.check(b, a) + b = "operator .sequenceIncludes(x, y)" + a = "operator .contains(x, y)" + self.check(b, a) + + b = "operator. sequenceIncludes(x, y)" + a = "operator. contains(x, y)" + self.check(b, a) + + def test_operator_isSequenceType(self): + b = "operator.isSequenceType(x)" + a = "import collections\nisinstance(x, collections.Sequence)" + self.check(b, a) + + def test_operator_isMappingType(self): + b = "operator.isMappingType(x)" + a = "import collections\nisinstance(x, collections.Mapping)" + self.check(b, a) + + def test_operator_isNumberType(self): + b = "operator.isNumberType(x)" + a = "import numbers\nisinstance(x, numbers.Number)" + self.check(b, a) + + def test_operator_repeat(self): + b = "operator.repeat(x, n)" + a = "operator.mul(x, n)" + self.check(b, a) + + b = "operator .repeat(x, n)" + a = "operator .mul(x, n)" + self.check(b, a) + + b = "operator. repeat(x, n)" + a = "operator. mul(x, n)" + self.check(b, a) + + def test_operator_irepeat(self): + b = "operator.irepeat(x, n)" + a = "operator.imul(x, n)" + self.check(b, a) + + b = "operator .irepeat(x, n)" + a = "operator .imul(x, n)" + self.check(b, a) + + b = "operator. irepeat(x, n)" + a = "operator. imul(x, n)" + self.check(b, a) + def test_bare_isCallable(self): s = "isCallable(x)" - self.warns_unchanged(s, "You should use hasattr(x, '__call__') here.") + t = "You should use 'hasattr(x, '__call__')' here." + self.warns_unchanged(s, t) def test_bare_sequenceIncludes(self): s = "sequenceIncludes(x, y)" - self.warns_unchanged(s, "You should use operator.contains here.") + t = "You should use 'operator.contains(x, y)' here." + self.warns_unchanged(s, t) + + def test_bare_operator_isSequenceType(self): + s = "isSequenceType(z)" + t = "You should use 'isinstance(z, collections.Sequence)' here." + self.warns_unchanged(s, t) + + def test_bare_operator_isMappingType(self): + s = "isMappingType(x)" + t = "You should use 'isinstance(x, collections.Mapping)' here." + self.warns_unchanged(s, t) + + def test_bare_operator_isNumberType(self): + s = "isNumberType(y)" + t = "You should use 'isinstance(y, numbers.Number)' here." + self.warns_unchanged(s, t) + + def test_bare_operator_repeat(self): + s = "repeat(x, n)" + t = "You should use 'operator.mul(x, n)' here." + self.warns_unchanged(s, t) + + def test_bare_operator_irepeat(self): + s = "irepeat(y, 187)" + t = "You should use 'operator.imul(y, 187)' here." + self.warns_unchanged(s, t) class Test_exitfunc(FixerTestCase): From python-checkins at python.org Thu Aug 5 09:12:19 2010 From: python-checkins at python.org (alexandre.vassalotti) Date: Thu, 5 Aug 2010 09:12:19 +0200 (CEST) Subject: [Python-checkins] r83741 - python/branches/py3k/Doc/library/2to3.rst Message-ID: <20100805071219.0D5A3EE989@mail.python.org> Author: alexandre.vassalotti Date: Thu Aug 5 09:12:18 2010 New Revision: 83741 Log: Issue 5077: Add documentation for operator fixer. Patch by Meador Inge. Modified: python/branches/py3k/Doc/library/2to3.rst Modified: python/branches/py3k/Doc/library/2to3.rst ============================================================================== --- python/branches/py3k/Doc/library/2to3.rst (original) +++ python/branches/py3k/Doc/library/2to3.rst Thu Aug 5 09:12:18 2010 @@ -267,6 +267,25 @@ Converts octal literals into the new syntax. +.. 2to3fixer:: operator + + Converts calls to various functions in the :mod:`operator` module to other, + but equivalent, function calls. When needed, the appropriate ``import`` + statements are added, e.g. ``import collections``. The following mapping + are made: + + ================================== ========================================== + From To + ================================== ========================================== + ``operator.isCallable(obj)`` ``hasattr(obj, '__call__')`` + ``operator.sequenceIncludes(obj)`` ``operator.contains(obj)`` + ``operator.isSequenceType(obj)`` ``isinstance(obj, collections.Sequence)`` + ``operator.isMappingType(obj)`` ``isinstance(obj, collections.Mapping)`` + ``operator.isNumberType(obj)`` ``isinstance(obj, numbers.Number)`` + ``operator.repeat(obj, n)`` ``operator.mul(obj, n)`` + ``operator.irepeat(obj, n)`` ``operator.imul(obj, n)`` + ================================== ========================================== + .. 2to3fixer:: paren Add extra parenthesis where they are required in list comprehensions. For From python-checkins at python.org Thu Aug 5 16:08:44 2010 From: python-checkins at python.org (gerhard.haering) Date: Thu, 5 Aug 2010 16:08:44 +0200 (CEST) Subject: [Python-checkins] r83742 - in python/branches/py3k: Lib/smtplib.py Misc/NEWS Message-ID: <20100805140844.71F41EE9B5@mail.python.org> Author: gerhard.haering Date: Thu Aug 5 16:08:44 2010 New Revision: 83742 Log: Issue #6683: For SMTP logins we now try all authentication methods advertised by the server. Many servers are buggy and advertise authentication methods they o not support in reality. This change makes smtplib.auth() work more often in the real world, where we face misconfigured servers and servers that advertise methods they don't support due to the madness that is SASL. Modified: python/branches/py3k/Lib/smtplib.py python/branches/py3k/Misc/NEWS Modified: python/branches/py3k/Lib/smtplib.py ============================================================================== --- python/branches/py3k/Lib/smtplib.py (original) +++ python/branches/py3k/Lib/smtplib.py Thu Aug 5 16:08:44 2010 @@ -559,43 +559,44 @@ if not self.has_extn("auth"): raise SMTPException("SMTP AUTH extension not supported by server.") - # Authentication methods the server supports: - authlist = self.esmtp_features["auth"].split() + # Authentication methods the server claims to support + advertised_authlist = self.esmtp_features["auth"].split() # List of authentication methods we support: from preferred to # less preferred methods. Except for the purpose of testing the weaker # ones, we prefer stronger methods like CRAM-MD5: preferred_auths = [AUTH_CRAM_MD5, AUTH_PLAIN, AUTH_LOGIN] - # Determine the authentication method we'll use - authmethod = None - for method in preferred_auths: - if method in authlist: - authmethod = method - break - - if authmethod == AUTH_CRAM_MD5: - (code, resp) = self.docmd("AUTH", AUTH_CRAM_MD5) - if code == 503: - # 503 == 'Error: already authenticated' - return (code, resp) - (code, resp) = self.docmd(encode_cram_md5(resp, user, password)) - elif authmethod == AUTH_PLAIN: - (code, resp) = self.docmd("AUTH", - AUTH_PLAIN + " " + encode_plain(user, password)) - elif authmethod == AUTH_LOGIN: - (code, resp) = self.docmd("AUTH", - "%s %s" % (AUTH_LOGIN, encode_base64(user.encode('ascii'), eol=''))) - if code != 334: - raise SMTPAuthenticationError(code, resp) - (code, resp) = self.docmd(encode_base64(password.encode('ascii'), eol='')) - elif authmethod is None: + # We try the authentication methods the server advertises, but only the + # ones *we* support. And in our preferred order. + authlist = [auth for auth in preferred_auths if auth in advertised_authlist] + if not authlist: raise SMTPException("No suitable authentication method found.") - if code not in (235, 503): + + # Some servers advertise authentication methods they don't really + # support, so if authentication fails, we continue until we've tried + # all methods. + for authmethod in authlist: + if authmethod == AUTH_CRAM_MD5: + (code, resp) = self.docmd("AUTH", AUTH_CRAM_MD5) + if code == 334: + (code, resp) = self.docmd(encode_cram_md5(resp, user, password)) + elif authmethod == AUTH_PLAIN: + (code, resp) = self.docmd("AUTH", + AUTH_PLAIN + " " + encode_plain(user, password)) + elif authmethod == AUTH_LOGIN: + (code, resp) = self.docmd("AUTH", + "%s %s" % (AUTH_LOGIN, encode_base64(user.encode('ascii'), eol=''))) + if code == 334: + (code, resp) = self.docmd(encode_base64(password.encode('ascii'), eol='')) + # 235 == 'Authentication successful' # 503 == 'Error: already authenticated' - raise SMTPAuthenticationError(code, resp) - return (code, resp) + if code in (235, 503): + return (code, resp) + + # We could not login sucessfully. Return result of last attempt. + raise SMTPAuthenticationError(code, resp) def starttls(self, keyfile = None, certfile = None): """Puts the connection to the SMTP server into TLS mode. Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Thu Aug 5 16:08:44 2010 @@ -40,6 +40,10 @@ Library ------- +- Issue #6683: For SMTP logins we now try all authentication methods advertised + by the server. Many servers are buggy and advertise authentication methods they + o not support in reality. + - Issue #8814: function annotations (the ``__annotations__`` attribute) are now included in the set of attributes copied by default by functools.wraps and functools.update_wrapper. Patch by Terrence Cole. From python-checkins at python.org Thu Aug 5 18:35:53 2010 From: python-checkins at python.org (matthias.klose) Date: Thu, 5 Aug 2010 18:35:53 +0200 (CEST) Subject: [Python-checkins] r83743 - in python/branches/release26-maint: Misc/NEWS Modules/_cursesmodule.c Message-ID: <20100805163553.6176CEE9B2@mail.python.org> Author: matthias.klose Date: Thu Aug 5 18:35:53 2010 New Revision: 83743 Log: Merged revisions 83306 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/release27-maint ........ r83306 | matthias.klose | 2010-07-30 23:27:18 +0200 (Fr, 30 Jul 2010) | 2 lines - Issue #7567: Don't call `setupterm' twice. ........ Modified: python/branches/release26-maint/ (props changed) python/branches/release26-maint/Misc/NEWS python/branches/release26-maint/Modules/_cursesmodule.c Modified: python/branches/release26-maint/Misc/NEWS ============================================================================== --- python/branches/release26-maint/Misc/NEWS (original) +++ python/branches/release26-maint/Misc/NEWS Thu Aug 5 18:35:53 2010 @@ -15,6 +15,11 @@ - Issue #5798: Handle select.poll flag oddities properly on OS X. This fixes test_asynchat and test_smtplib failures on OS X. +Extension Modules +----------------- + +- Issue #7567: Don't call `setupterm' twice. + What's New in Python 2.6.6 rc 1? ================================ Modified: python/branches/release26-maint/Modules/_cursesmodule.c ============================================================================== --- python/branches/release26-maint/Modules/_cursesmodule.c (original) +++ python/branches/release26-maint/Modules/_cursesmodule.c Thu Aug 5 18:35:53 2010 @@ -2039,7 +2039,7 @@ } } - if (setupterm(termstr,fd,&err) == ERR) { + if (!initialised_setupterm && setupterm(termstr,fd,&err) == ERR) { char* s = "setupterm: unknown error"; if (err == 0) { From python-checkins at python.org Thu Aug 5 19:34:32 2010 From: python-checkins at python.org (alexander.belopolsky) Date: Thu, 5 Aug 2010 19:34:32 +0200 (CEST) Subject: [Python-checkins] r83744 - in python/branches/py3k: Include/Python.h Include/pytime.h Makefile.pre.in Modules/_datetimemodule.c Modules/timemodule.c PCbuild/pythoncore.vcproj Python/pythonrun.c Python/pytime.c Message-ID: <20100805173432.1A180F1B3@mail.python.org> Author: alexander.belopolsky Date: Thu Aug 5 19:34:27 2010 New Revision: 83744 Log: Issue #9079: Added _PyTime_gettimeofday(_PyTime_timeval *tp) to C API exposed in Python.h. This function is similar to POSIX gettimeofday(struct timeval *tp), but available on platforms without gettimeofday(). Added: python/branches/py3k/Include/pytime.h python/branches/py3k/Python/pytime.c Modified: python/branches/py3k/Include/Python.h python/branches/py3k/Makefile.pre.in python/branches/py3k/Modules/_datetimemodule.c python/branches/py3k/Modules/timemodule.c python/branches/py3k/PCbuild/pythoncore.vcproj python/branches/py3k/Python/pythonrun.c Modified: python/branches/py3k/Include/Python.h ============================================================================== --- python/branches/py3k/Include/Python.h (original) +++ python/branches/py3k/Include/Python.h Thu Aug 5 19:34:27 2010 @@ -61,6 +61,7 @@ #error "PYMALLOC_DEBUG requires WITH_PYMALLOC" #endif #include "pymath.h" +#include "pytime.h" #include "pymem.h" #include "object.h" Added: python/branches/py3k/Include/pytime.h ============================================================================== --- (empty file) +++ python/branches/py3k/Include/pytime.h Thu Aug 5 19:34:27 2010 @@ -0,0 +1,70 @@ +#ifndef Py_PYTIME_H +#define Py_PYTIME_H + +#include "pyconfig.h" /* include for defines */ + +/************************************************************************** +Symbols and macros to supply platform-independent interfaces to time related +functions and constants +**************************************************************************/ +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef HAVE_GETTIMEOFDAY +typedef struct timeval _PyTime_timeval; +#else +typedef struct { + time_t tv_sec; /* seconds since Jan. 1, 1970 */ + long tv_usec; /* and microseconds */ +} _PyTime_timeval; +#endif + +/* Similar to POSIX gettimeofday but cannot fail. If system gettimeofday + * fails or is not available, fall back to lower resolution clocks. + */ +PyAPI_FUNC(void) _PyTime_gettimeofday(_PyTime_timeval *tp); + +/* Dummy to force linking. */ +PyAPI_FUNC(void) _PyTime_Init(void); + +#ifdef __cplusplus +} +#endif + +#endif /* Py_PYTIME_H */ +#ifndef Py_PYTIME_H +#define Py_PYTIME_H + +#include "pyconfig.h" /* include for defines */ + +/************************************************************************** +Symbols and macros to supply platform-independent interfaces to time related +functions and constants +**************************************************************************/ +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef HAVE_GETTIMEOFDAY +typedef struct timeval _PyTime_timeval; +#else +typedef struct { + time_t tv_sec; /* seconds since Jan. 1, 1970 */ + long tv_usec; /* and microseconds */ +} _PyTime_timeval; +#endif + +/* Similar to POSIX gettimeofday but cannot fail. If system gettimeofday + * fails or is not available, fall back to lower resolution clocks. + */ +PyAPI_FUNC(void) _PyTime_gettimeofday(_PyTime_timeval *tp); + +/* Dummy to force linking. */ +PyAPI_FUNC(void) _PyTime_Init(void); + +#ifdef __cplusplus +} +#endif + +#endif /* Py_PYTIME_H */ Modified: python/branches/py3k/Makefile.pre.in ============================================================================== --- python/branches/py3k/Makefile.pre.in (original) +++ python/branches/py3k/Makefile.pre.in Thu Aug 5 19:34:27 2010 @@ -314,6 +314,7 @@ Python/pymath.o \ Python/pystate.o \ Python/pythonrun.o \ + Python/pytime.o \ Python/structmember.o \ Python/symtable.o \ Python/sysmodule.o \ @@ -696,6 +697,7 @@ Include/pystrtod.h \ Include/pythonrun.h \ Include/pythread.h \ + Include/pytime.h \ Include/rangeobject.h \ Include/setobject.h \ Include/sliceobject.h \ Modified: python/branches/py3k/Modules/_datetimemodule.c ============================================================================== --- python/branches/py3k/Modules/_datetimemodule.c (original) +++ python/branches/py3k/Modules/_datetimemodule.c Thu Aug 5 19:34:27 2010 @@ -8,7 +8,7 @@ #include -#include "timefuncs.h" +#include "_time.h" /* Differentiate between building the core module and building extension * modules. @@ -4166,37 +4166,10 @@ static PyObject * datetime_best_possible(PyObject *cls, TM_FUNC f, PyObject *tzinfo) { -#ifdef HAVE_GETTIMEOFDAY - struct timeval t; - -#ifdef GETTIMEOFDAY_NO_TZ - gettimeofday(&t); -#else - gettimeofday(&t, (struct timezone *)NULL); -#endif + _PyTime_timeval t; + _PyTime_gettimeofday(&t); return datetime_from_timet_and_us(cls, f, t.tv_sec, (int)t.tv_usec, tzinfo); - -#else /* ! HAVE_GETTIMEOFDAY */ - /* No flavor of gettimeofday exists on this platform. Python's - * time.time() does a lot of other platform tricks to get the - * best time it can on the platform, and we're not going to do - * better than that (if we could, the better code would belong - * in time.time()!) We're limited by the precision of a double, - * though. - */ - PyObject *time; - double dtime; - - time = time_time(); - if (time == NULL) - return NULL; - dtime = PyFloat_AsDouble(time); - Py_DECREF(time); - if (dtime == -1.0 && PyErr_Occurred()) - return NULL; - return datetime_from_timestamp(cls, f, dtime, tzinfo); -#endif /* ! HAVE_GETTIMEOFDAY */ } /* Return best possible local time -- this isn't constrained by the Modified: python/branches/py3k/Modules/timemodule.c ============================================================================== --- python/branches/py3k/Modules/timemodule.c (original) +++ python/branches/py3k/Modules/timemodule.c Thu Aug 5 19:34:27 2010 @@ -3,22 +3,10 @@ #include "Python.h" #include "structseq.h" -#include "timefuncs.h" +#include "_time.h" #define TZNAME_ENCODING "utf-8" -#ifdef __APPLE__ -#if defined(HAVE_GETTIMEOFDAY) && defined(HAVE_FTIME) - /* - * floattime falls back to ftime when getttimeofday fails because the latter - * might fail on some platforms. This fallback is unwanted on MacOSX because - * that makes it impossible to use a binary build on OSX 10.4 on earlier - * releases of the OS. Therefore claim we don't support ftime. - */ -# undef HAVE_FTIME -#endif -#endif - #include #ifdef HAVE_SYS_TYPES_H @@ -29,13 +17,6 @@ #include #endif -#ifdef HAVE_FTIME -#include -#if !defined(MS_WINDOWS) && !defined(PYOS_OS2) -extern int ftime(struct timeb *); -#endif /* MS_WINDOWS */ -#endif /* HAVE_FTIME */ - #if defined(__WATCOMC__) && !defined(__QNX__) #include #else @@ -946,44 +927,12 @@ return m; } - -/* Implement floattime() for various platforms */ - static double floattime(void) { - /* There are three ways to get the time: - (1) gettimeofday() -- resolution in microseconds - (2) ftime() -- resolution in milliseconds - (3) time() -- resolution in seconds - In all cases the return value is a float in seconds. - Since on some systems (e.g. SCO ODT 3.0) gettimeofday() may - fail, so we fall back on ftime() or time(). - Note: clock resolution does not imply clock accuracy! */ -#ifdef HAVE_GETTIMEOFDAY - { - struct timeval t; -#ifdef GETTIMEOFDAY_NO_TZ - if (gettimeofday(&t) == 0) - return (double)t.tv_sec + t.tv_usec*0.000001; -#else /* !GETTIMEOFDAY_NO_TZ */ - if (gettimeofday(&t, (struct timezone *)NULL) == 0) - return (double)t.tv_sec + t.tv_usec*0.000001; -#endif /* !GETTIMEOFDAY_NO_TZ */ - } - -#endif /* !HAVE_GETTIMEOFDAY */ - { -#if defined(HAVE_FTIME) - struct timeb t; - ftime(&t); - return (double)t.time + (double)t.millitm * (double)0.001; -#else /* !HAVE_FTIME */ - time_t secs; - time(&secs); - return (double)secs; -#endif /* !HAVE_FTIME */ - } + _PyTime_timeval t; + _PyTime_gettimeofday(&t); + return (double)t.tv_sec + t.tv_usec*0.000001; } Modified: python/branches/py3k/PCbuild/pythoncore.vcproj ============================================================================== --- python/branches/py3k/PCbuild/pythoncore.vcproj (original) +++ python/branches/py3k/PCbuild/pythoncore.vcproj Thu Aug 5 19:34:27 2010 @@ -876,6 +876,10 @@ > + + @@ -1768,6 +1772,10 @@ > + + Modified: python/branches/py3k/Python/pythonrun.c ============================================================================== --- python/branches/py3k/Python/pythonrun.c (original) +++ python/branches/py3k/Python/pythonrun.c Thu Aug 5 19:34:27 2010 @@ -268,6 +268,8 @@ /* Initialize _warnings. */ _PyWarnings_Init(); + _PyTime_Init(); + initfsencoding(); if (install_sigs) Added: python/branches/py3k/Python/pytime.c ============================================================================== --- (empty file) +++ python/branches/py3k/Python/pytime.c Thu Aug 5 19:34:27 2010 @@ -0,0 +1,60 @@ +#include "Python.h" + +#ifdef __APPLE__ +#if defined(HAVE_GETTIMEOFDAY) && defined(HAVE_FTIME) + /* + * _PyTime_gettimeofday falls back to ftime when getttimeofday fails because the latter + * might fail on some platforms. This fallback is unwanted on MacOSX because + * that makes it impossible to use a binary build on OSX 10.4 on earlier + * releases of the OS. Therefore claim we don't support ftime. + */ +# undef HAVE_FTIME +#endif +#endif + +#ifdef HAVE_FTIME +#include +#if !defined(MS_WINDOWS) && !defined(PYOS_OS2) +extern int ftime(struct timeb *); +#endif /* MS_WINDOWS */ +#endif /* HAVE_FTIME */ + +void +_PyTime_gettimeofday(_PyTime_timeval *tp) +{ + /* There are three ways to get the time: + (1) gettimeofday() -- resolution in microseconds + (2) ftime() -- resolution in milliseconds + (3) time() -- resolution in seconds + In all cases the return value in a timeval struct. + Since on some systems (e.g. SCO ODT 3.0) gettimeofday() may + fail, so we fall back on ftime() or time(). + Note: clock resolution does not imply clock accuracy! */ +#ifdef HAVE_GETTIMEOFDAY +#ifdef GETTIMEOFDAY_NO_TZ + if (gettimeofday(tp) == 0) + return; +#else /* !GETTIMEOFDAY_NO_TZ */ + if (gettimeofday(tp, (struct timezone *)NULL) == 0) + return; +#endif /* !GETTIMEOFDAY_NO_TZ */ +#endif /* !HAVE_GETTIMEOFDAY */ +#if defined(HAVE_FTIME) + { + struct timeb t; + ftime(&t); + tp->tv_sec = t.time; + tp->tv_usec = t.millitm * 1000; + } +#else /* !HAVE_FTIME */ + tp->tv_sec = time(NULL); + tp->tv_usec = 0; +#endif /* !HAVE_FTIME */ + return; +} + +void +_PyTime_Init() +{ + /* Do nothing. Needed to force linking. */ +} From python-checkins at python.org Thu Aug 5 20:56:01 2010 From: python-checkins at python.org (brian.curtin) Date: Thu, 5 Aug 2010 20:56:01 +0200 (CEST) Subject: [Python-checkins] r83745 - in python/branches/py3k: Doc/library/signal.rst Misc/NEWS Message-ID: <20100805185601.247A0EE9DA@mail.python.org> Author: brian.curtin Date: Thu Aug 5 20:56:00 2010 New Revision: 83745 Log: Issue #9524: Document that two CTRL* signals are meant for use only with os.kill. Modified: python/branches/py3k/Doc/library/signal.rst python/branches/py3k/Misc/NEWS Modified: python/branches/py3k/Doc/library/signal.rst ============================================================================== --- python/branches/py3k/Doc/library/signal.rst (original) +++ python/branches/py3k/Doc/library/signal.rst Thu Aug 5 20:56:00 2010 @@ -76,7 +76,9 @@ .. data:: CTRL_C_EVENT - The signal corresponding to the CTRL+C keystroke event. + The signal corresponding to the CTRL+C keystroke event. This signal can + only be used with :func:`os.kill`. + Availability: Windows. .. versionadded:: 3.2 @@ -84,7 +86,9 @@ .. data:: CTRL_BREAK_EVENT - The signal corresponding to the CTRL+BREAK keystroke event. + The signal corresponding to the CTRL+BREAK keystroke event. This signal can + only be used with :func:`os.kill`. + Availability: Windows. .. versionadded:: 3.2 Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Thu Aug 5 20:56:00 2010 @@ -1849,6 +1849,9 @@ Documentation ------------ +- Issue #9524: Document that two CTRL* signals are meant for use only + with os.kill. + - Issue #9255: Document that the 'test' package is meant for internal Python use only. From python-checkins at python.org Thu Aug 5 21:00:45 2010 From: python-checkins at python.org (brian.curtin) Date: Thu, 5 Aug 2010 21:00:45 +0200 (CEST) Subject: [Python-checkins] r83746 - in python/branches/release27-maint: Doc/library/signal.rst Misc/NEWS Message-ID: <20100805190045.A935FEE987@mail.python.org> Author: brian.curtin Date: Thu Aug 5 21:00:45 2010 New Revision: 83746 Log: Merged revisions 83745 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83745 | brian.curtin | 2010-08-05 13:56:00 -0500 (Thu, 05 Aug 2010) | 4 lines Issue #9524: Document that two CTRL* signals are meant for use only with os.kill. ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Doc/library/signal.rst python/branches/release27-maint/Misc/NEWS Modified: python/branches/release27-maint/Doc/library/signal.rst ============================================================================== --- python/branches/release27-maint/Doc/library/signal.rst (original) +++ python/branches/release27-maint/Doc/library/signal.rst Thu Aug 5 21:00:45 2010 @@ -77,7 +77,9 @@ .. data:: CTRL_C_EVENT - The signal corresponding to the CTRL+C keystroke event. + The signal corresponding to the CTRL+C keystroke event. This signal can + only be used with :func:`os.kill`. + Availability: Windows. .. versionadded:: 2.7 @@ -85,7 +87,9 @@ .. data:: CTRL_BREAK_EVENT - The signal corresponding to the CTRL+BREAK keystroke event. + The signal corresponding to the CTRL+BREAK keystroke event. This signal can + only be used with :func:`os.kill`. + Availability: Windows. .. versionadded:: 2.7 Modified: python/branches/release27-maint/Misc/NEWS ============================================================================== --- python/branches/release27-maint/Misc/NEWS (original) +++ python/branches/release27-maint/Misc/NEWS Thu Aug 5 21:00:45 2010 @@ -193,6 +193,9 @@ Documentation ------------- +- Issue #9524: Document that two CTRL* signals are meant for use only + with os.kill. + - Issue #9255: Document that the 'test' package is for internal Python use only. From solipsis at pitrou.net Fri Aug 6 05:04:58 2010 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Fri, 06 Aug 2010 05:04:58 +0200 Subject: [Python-checkins] Daily py3k reference leaks (r83745): sum=0 Message-ID: py3k results for svn r83745 (hg cset ea07938d453c) -------------------------------------------------- Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/py3k/refleaks/reflogWf5PMG', '-x'] From python-checkins at python.org Fri Aug 6 08:12:05 2010 From: python-checkins at python.org (gerhard.haering) Date: Fri, 6 Aug 2010 08:12:05 +0200 (CEST) Subject: [Python-checkins] r83747 - python/branches/py3k/Doc/library/sqlite3.rst Message-ID: <20100806061205.DAFB3F5AD@mail.python.org> Author: gerhard.haering Date: Fri Aug 6 08:12:05 2010 New Revision: 83747 Log: Issue #3854: Documented using the sqlite3 module with multiple threads. Modified: python/branches/py3k/Doc/library/sqlite3.rst Modified: python/branches/py3k/Doc/library/sqlite3.rst ============================================================================== --- python/branches/py3k/Doc/library/sqlite3.rst (original) +++ python/branches/py3k/Doc/library/sqlite3.rst Fri Aug 6 08:12:05 2010 @@ -867,3 +867,18 @@ committed: .. literalinclude:: ../includes/sqlite3/ctx_manager.py + + +Common issues +------------- + +Multithreading +^^^^^^^^^^^^^^ + +Older SQLite versions had issues with sharing connections between threads. +That's why the Python module disallows sharing connections and cursors between +threads. If you still try to do so, you will get an exception at runtime. + +The only exception is calling the :meth:`~Connection.interrupt` method, which +only makes sense to call from a different thread. + From python-checkins at python.org Fri Aug 6 08:13:25 2010 From: python-checkins at python.org (gerhard.haering) Date: Fri, 6 Aug 2010 08:13:25 +0200 (CEST) Subject: [Python-checkins] r83748 - python/branches/release31-maint/Doc/library/sqlite3.rst Message-ID: <20100806061325.8C08EEBB7@mail.python.org> Author: gerhard.haering Date: Fri Aug 6 08:13:25 2010 New Revision: 83748 Log: Issue #3854: Documented using the sqlite3 module with multiple threads. Modified: python/branches/release31-maint/Doc/library/sqlite3.rst Modified: python/branches/release31-maint/Doc/library/sqlite3.rst ============================================================================== --- python/branches/release31-maint/Doc/library/sqlite3.rst (original) +++ python/branches/release31-maint/Doc/library/sqlite3.rst Fri Aug 6 08:13:25 2010 @@ -840,3 +840,18 @@ committed: .. literalinclude:: ../includes/sqlite3/ctx_manager.py + + +Common issues +------------- + +Multithreading +^^^^^^^^^^^^^^ + +Older SQLite versions had issues with sharing connections between threads. +That's why the Python module disallows sharing connections and cursors between +threads. If you still try to do so, you will get an exception at runtime. + +The only exception is calling the :meth:`~Connection.interrupt` method, which +only makes sense to call from a different thread. + From python-checkins at python.org Fri Aug 6 08:14:12 2010 From: python-checkins at python.org (gerhard.haering) Date: Fri, 6 Aug 2010 08:14:12 +0200 (CEST) Subject: [Python-checkins] r83749 - python/branches/release27-maint/Doc/library/sqlite3.rst Message-ID: <20100806061412.56825E96E@mail.python.org> Author: gerhard.haering Date: Fri Aug 6 08:14:12 2010 New Revision: 83749 Log: Issue #3854: Documented using the sqlite3 module with multiple threads. Modified: python/branches/release27-maint/Doc/library/sqlite3.rst Modified: python/branches/release27-maint/Doc/library/sqlite3.rst ============================================================================== --- python/branches/release27-maint/Doc/library/sqlite3.rst (original) +++ python/branches/release27-maint/Doc/library/sqlite3.rst Fri Aug 6 08:14:12 2010 @@ -880,3 +880,18 @@ committed: .. literalinclude:: ../includes/sqlite3/ctx_manager.py + + +Common issues +------------- + +Multithreading +^^^^^^^^^^^^^^ + +Older SQLite versions had issues with sharing connections between threads. +That's why the Python module disallows sharing connections and cursors between +threads. If you still try to do so, you will get an exception at runtime. + +The only exception is calling the :meth:`~Connection.interrupt` method, which +only makes sense to call from a different thread. + From python-checkins at python.org Fri Aug 6 11:22:47 2010 From: python-checkins at python.org (raymond.hettinger) Date: Fri, 6 Aug 2010 11:22:47 +0200 (CEST) Subject: [Python-checkins] r83750 - python/branches/py3k/PC/VS8.0/pythoncore.vcproj Message-ID: <20100806092247.0D246F62B@mail.python.org> Author: raymond.hettinger Date: Fri Aug 6 11:22:46 2010 New Revision: 83750 Log: Fix the VS8.0 build Modified: python/branches/py3k/PC/VS8.0/pythoncore.vcproj Modified: python/branches/py3k/PC/VS8.0/pythoncore.vcproj ============================================================================== --- python/branches/py3k/PC/VS8.0/pythoncore.vcproj (original) +++ python/branches/py3k/PC/VS8.0/pythoncore.vcproj Fri Aug 6 11:22:46 2010 @@ -879,6 +879,10 @@ > + + @@ -1767,6 +1771,10 @@ > + + From python-checkins at python.org Fri Aug 6 11:36:57 2010 From: python-checkins at python.org (mark.dickinson) Date: Fri, 6 Aug 2010 11:36:57 +0200 (CEST) Subject: [Python-checkins] r83751 - python/branches/py3k/Modules/arraymodule.c Message-ID: <20100806093657.A00E6EE9C8@mail.python.org> Author: mark.dickinson Date: Fri Aug 6 11:36:57 2010 New Revision: 83751 Log: Issue #9526: Remove outdated casts to int that were preventing the array module from working correctly with arrays > 2GB. Modified: python/branches/py3k/Modules/arraymodule.c Modified: python/branches/py3k/Modules/arraymodule.c ============================================================================== --- python/branches/py3k/Modules/arraymodule.c (original) +++ python/branches/py3k/Modules/arraymodule.c Fri Aug 6 11:36:57 2010 @@ -800,7 +800,7 @@ return -1; while ((v = PyIter_Next(it)) != NULL) { - if (ins1(self, (int) Py_SIZE(self), v) != 0) { + if (ins1(self, Py_SIZE(self), v) != 0) { Py_DECREF(v); Py_DECREF(it); return -1; @@ -1075,7 +1075,7 @@ static PyObject * array_append(arrayobject *self, PyObject *v) { - return ins(self, (int) Py_SIZE(self), v); + return ins(self, Py_SIZE(self), v); } PyDoc_STRVAR(append_doc, From python-checkins at python.org Fri Aug 6 11:38:58 2010 From: python-checkins at python.org (mark.dickinson) Date: Fri, 6 Aug 2010 11:38:58 +0200 (CEST) Subject: [Python-checkins] r83752 - python/branches/py3k/Misc/NEWS Message-ID: <20100806093858.61C3ACA78@mail.python.org> Author: mark.dickinson Date: Fri Aug 6 11:38:58 2010 New Revision: 83752 Log: Misc/NEWS entry for r83751. Modified: python/branches/py3k/Misc/NEWS Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Fri Aug 6 11:38:58 2010 @@ -24,6 +24,10 @@ Extensions ---------- +- Issue #9526: Remove some outdated (int) casts that were preventing + the array module from working correctly with arrays of more than + 2**31 elements. + - Fix memory leak in ssl._ssl._test_decode_cert. - Issue #8065: Fix memory leak in readline module (from failure to From python-checkins at python.org Fri Aug 6 11:42:28 2010 From: python-checkins at python.org (mark.dickinson) Date: Fri, 6 Aug 2010 11:42:28 +0200 (CEST) Subject: [Python-checkins] r83753 - in python/branches/release31-maint: Misc/NEWS Modules/arraymodule.c Message-ID: <20100806094228.44F28D976@mail.python.org> Author: mark.dickinson Date: Fri Aug 6 11:42:28 2010 New Revision: 83753 Log: Merged revisions 83751-83752 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83751 | mark.dickinson | 2010-08-06 10:36:57 +0100 (Fri, 06 Aug 2010) | 1 line Issue #9526: Remove outdated casts to int that were preventing the array module from working correctly with arrays > 2GB. ........ r83752 | mark.dickinson | 2010-08-06 10:38:58 +0100 (Fri, 06 Aug 2010) | 1 line Misc/NEWS entry for r83751. ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Misc/NEWS python/branches/release31-maint/Modules/arraymodule.c Modified: python/branches/release31-maint/Misc/NEWS ============================================================================== --- python/branches/release31-maint/Misc/NEWS (original) +++ python/branches/release31-maint/Misc/NEWS Fri Aug 6 11:42:28 2010 @@ -392,6 +392,10 @@ Extension Modules ----------------- +- Issue #9526: Remove some outdated (int) casts that were preventing + the array module from working correctly with arrays of more than + 2**31 elements. + - Fix memory leak in ssl._ssl._test_decode_cert. - Issue #8065: Fix memory leak in readline module (from failure to Modified: python/branches/release31-maint/Modules/arraymodule.c ============================================================================== --- python/branches/release31-maint/Modules/arraymodule.c (original) +++ python/branches/release31-maint/Modules/arraymodule.c Fri Aug 6 11:42:28 2010 @@ -794,7 +794,7 @@ return -1; while ((v = PyIter_Next(it)) != NULL) { - if (ins1(self, (int) Py_SIZE(self), v) != 0) { + if (ins1(self, Py_SIZE(self), v) != 0) { Py_DECREF(v); Py_DECREF(it); return -1; @@ -1069,7 +1069,7 @@ static PyObject * array_append(arrayobject *self, PyObject *v) { - return ins(self, (int) Py_SIZE(self), v); + return ins(self, Py_SIZE(self), v); } PyDoc_STRVAR(append_doc, From python-checkins at python.org Fri Aug 6 11:44:48 2010 From: python-checkins at python.org (mark.dickinson) Date: Fri, 6 Aug 2010 11:44:48 +0200 (CEST) Subject: [Python-checkins] r83754 - in python/branches/release27-maint: Misc/NEWS Modules/arraymodule.c Message-ID: <20100806094448.CF957EE9B8@mail.python.org> Author: mark.dickinson Date: Fri Aug 6 11:44:48 2010 New Revision: 83754 Log: Merged revisions 83751-83752 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83751 | mark.dickinson | 2010-08-06 10:36:57 +0100 (Fri, 06 Aug 2010) | 1 line Issue #9526: Remove outdated casts to int that were preventing the array module from working correctly with arrays > 2GB. ........ r83752 | mark.dickinson | 2010-08-06 10:38:58 +0100 (Fri, 06 Aug 2010) | 1 line Misc/NEWS entry for r83751. ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Misc/NEWS python/branches/release27-maint/Modules/arraymodule.c Modified: python/branches/release27-maint/Misc/NEWS ============================================================================== --- python/branches/release27-maint/Misc/NEWS (original) +++ python/branches/release27-maint/Misc/NEWS Fri Aug 6 11:44:48 2010 @@ -142,6 +142,10 @@ Extension Modules ----------------- +- Issue #9526: Remove some outdated (int) casts that were preventing + the array module from working correctly with arrays of more than + 2**31 elements. + - Fix memory leak in ssl._ssl._test_decode_cert. - Issue #8065: Fix memory leak in readline module (from failure to Modified: python/branches/release27-maint/Modules/arraymodule.c ============================================================================== --- python/branches/release27-maint/Modules/arraymodule.c (original) +++ python/branches/release27-maint/Modules/arraymodule.c Fri Aug 6 11:44:48 2010 @@ -798,7 +798,7 @@ return -1; while ((v = PyIter_Next(it)) != NULL) { - if (ins1(self, (int) Py_SIZE(self), v) != 0) { + if (ins1(self, Py_SIZE(self), v) != 0) { Py_DECREF(v); Py_DECREF(it); return -1; @@ -1090,7 +1090,7 @@ static PyObject * array_append(arrayobject *self, PyObject *v) { - return ins(self, (int) Py_SIZE(self), v); + return ins(self, Py_SIZE(self), v); } PyDoc_STRVAR(append_doc, From python-checkins at python.org Fri Aug 6 11:52:17 2010 From: python-checkins at python.org (raymond.hettinger) Date: Fri, 6 Aug 2010 11:52:17 +0200 (CEST) Subject: [Python-checkins] r83755 - python/branches/py3k/Objects/setobject.c Message-ID: <20100806095217.81350EE9AD@mail.python.org> Author: raymond.hettinger Date: Fri Aug 6 11:52:17 2010 New Revision: 83755 Log: Issue8757: Implicit set-to-frozenset conversion not thread-safe. Modified: python/branches/py3k/Objects/setobject.c Modified: python/branches/py3k/Objects/setobject.c ============================================================================== --- python/branches/py3k/Objects/setobject.c (original) +++ python/branches/py3k/Objects/setobject.c Fri Aug 6 11:52:17 2010 @@ -1846,12 +1846,10 @@ if (!PySet_Check(key) || !PyErr_ExceptionMatches(PyExc_TypeError)) return -1; PyErr_Clear(); - tmpkey = make_new_set(&PyFrozenSet_Type, NULL); + tmpkey = make_new_set(&PyFrozenSet_Type, key); if (tmpkey == NULL) return -1; - set_swap_bodies((PySetObject *)tmpkey, (PySetObject *)key); rv = set_contains(so, tmpkey); - set_swap_bodies((PySetObject *)tmpkey, (PySetObject *)key); Py_DECREF(tmpkey); } return rv; @@ -1881,12 +1879,10 @@ if (!PySet_Check(key) || !PyErr_ExceptionMatches(PyExc_TypeError)) return NULL; PyErr_Clear(); - tmpkey = make_new_set(&PyFrozenSet_Type, NULL); + tmpkey = make_new_set(&PyFrozenSet_Type, key); if (tmpkey == NULL) return NULL; - set_swap_bodies((PySetObject *)tmpkey, (PySetObject *)key); rv = set_discard_key(so, tmpkey); - set_swap_bodies((PySetObject *)tmpkey, (PySetObject *)key); Py_DECREF(tmpkey); if (rv == -1) return NULL; @@ -1915,12 +1911,10 @@ if (!PySet_Check(key) || !PyErr_ExceptionMatches(PyExc_TypeError)) return NULL; PyErr_Clear(); - tmpkey = make_new_set(&PyFrozenSet_Type, NULL); + tmpkey = make_new_set(&PyFrozenSet_Type, key); if (tmpkey == NULL) return NULL; - set_swap_bodies((PySetObject *)tmpkey, (PySetObject *)key); result = set_discard(so, tmpkey); - set_swap_bodies((PySetObject *)tmpkey, (PySetObject *)key); Py_DECREF(tmpkey); return result; } From python-checkins at python.org Fri Aug 6 11:57:49 2010 From: python-checkins at python.org (raymond.hettinger) Date: Fri, 6 Aug 2010 11:57:49 +0200 (CEST) Subject: [Python-checkins] r83756 - in python/branches/release31-maint: Misc/NEWS Objects/setobject.c Message-ID: <20100806095749.A2C28F804@mail.python.org> Author: raymond.hettinger Date: Fri Aug 6 11:57:49 2010 New Revision: 83756 Log: Issue8757: Implicit set-to-frozenset conversion not thread-safe. Modified: python/branches/release31-maint/Misc/NEWS python/branches/release31-maint/Objects/setobject.c Modified: python/branches/release31-maint/Misc/NEWS ============================================================================== --- python/branches/release31-maint/Misc/NEWS (original) +++ python/branches/release31-maint/Misc/NEWS Fri Aug 6 11:57:49 2010 @@ -12,6 +12,8 @@ Core and Builtins ----------------- +- Issue #83755: Implicit set-to-frozenset conversion was not thread-safe. + - Issue #9416: Fix some issues with complex formatting where the output with no type specifier failed to match the str output: Modified: python/branches/release31-maint/Objects/setobject.c ============================================================================== --- python/branches/release31-maint/Objects/setobject.c (original) +++ python/branches/release31-maint/Objects/setobject.c Fri Aug 6 11:57:49 2010 @@ -1846,12 +1846,10 @@ if (!PySet_Check(key) || !PyErr_ExceptionMatches(PyExc_TypeError)) return -1; PyErr_Clear(); - tmpkey = make_new_set(&PyFrozenSet_Type, NULL); + tmpkey = make_new_set(&PyFrozenSet_Type, key); if (tmpkey == NULL) return -1; - set_swap_bodies((PySetObject *)tmpkey, (PySetObject *)key); rv = set_contains(so, tmpkey); - set_swap_bodies((PySetObject *)tmpkey, (PySetObject *)key); Py_DECREF(tmpkey); } return rv; @@ -1881,12 +1879,10 @@ if (!PySet_Check(key) || !PyErr_ExceptionMatches(PyExc_TypeError)) return NULL; PyErr_Clear(); - tmpkey = make_new_set(&PyFrozenSet_Type, NULL); + tmpkey = make_new_set(&PyFrozenSet_Type, key); if (tmpkey == NULL) return NULL; - set_swap_bodies((PySetObject *)tmpkey, (PySetObject *)key); rv = set_discard_key(so, tmpkey); - set_swap_bodies((PySetObject *)tmpkey, (PySetObject *)key); Py_DECREF(tmpkey); if (rv == -1) return NULL; @@ -1915,12 +1911,10 @@ if (!PySet_Check(key) || !PyErr_ExceptionMatches(PyExc_TypeError)) return NULL; PyErr_Clear(); - tmpkey = make_new_set(&PyFrozenSet_Type, NULL); + tmpkey = make_new_set(&PyFrozenSet_Type, key); if (tmpkey == NULL) return NULL; - set_swap_bodies((PySetObject *)tmpkey, (PySetObject *)key); result = set_discard(so, tmpkey); - set_swap_bodies((PySetObject *)tmpkey, (PySetObject *)key); Py_DECREF(tmpkey); return result; } From python-checkins at python.org Fri Aug 6 12:18:56 2010 From: python-checkins at python.org (raymond.hettinger) Date: Fri, 6 Aug 2010 12:18:56 +0200 (CEST) Subject: [Python-checkins] r83757 - in python/branches/release27-maint: Misc/NEWS Objects/setobject.c Message-ID: <20100806101856.EF764EE9CA@mail.python.org> Author: raymond.hettinger Date: Fri Aug 6 12:18:56 2010 New Revision: 83757 Log: Issue8757: Implicit set-to-frozenset conversion not thread-safe. Modified: python/branches/release27-maint/Misc/NEWS python/branches/release27-maint/Objects/setobject.c Modified: python/branches/release27-maint/Misc/NEWS ============================================================================== --- python/branches/release27-maint/Misc/NEWS (original) +++ python/branches/release27-maint/Misc/NEWS Fri Aug 6 12:18:56 2010 @@ -12,6 +12,8 @@ Core and Builtins ----------------- +- Issue #83755: Implicit set-to-frozenset conversion was not thread-safe. + - Issue #9416: Fix some issues with complex formatting where the output with no type specifier failed to match the str output: Modified: python/branches/release27-maint/Objects/setobject.c ============================================================================== --- python/branches/release27-maint/Objects/setobject.c (original) +++ python/branches/release27-maint/Objects/setobject.c Fri Aug 6 12:18:56 2010 @@ -1855,12 +1855,10 @@ if (!PySet_Check(key) || !PyErr_ExceptionMatches(PyExc_TypeError)) return -1; PyErr_Clear(); - tmpkey = make_new_set(&PyFrozenSet_Type, NULL); + tmpkey = make_new_set(&PyFrozenSet_Type, key); if (tmpkey == NULL) return -1; - set_swap_bodies((PySetObject *)tmpkey, (PySetObject *)key); rv = set_contains(so, tmpkey); - set_swap_bodies((PySetObject *)tmpkey, (PySetObject *)key); Py_DECREF(tmpkey); } return rv; @@ -1890,12 +1888,10 @@ if (!PySet_Check(key) || !PyErr_ExceptionMatches(PyExc_TypeError)) return NULL; PyErr_Clear(); - tmpkey = make_new_set(&PyFrozenSet_Type, NULL); + tmpkey = make_new_set(&PyFrozenSet_Type, key); if (tmpkey == NULL) return NULL; - set_swap_bodies((PySetObject *)tmpkey, (PySetObject *)key); rv = set_discard_key(so, tmpkey); - set_swap_bodies((PySetObject *)tmpkey, (PySetObject *)key); Py_DECREF(tmpkey); if (rv == -1) return NULL; @@ -1924,12 +1920,10 @@ if (!PySet_Check(key) || !PyErr_ExceptionMatches(PyExc_TypeError)) return NULL; PyErr_Clear(); - tmpkey = make_new_set(&PyFrozenSet_Type, NULL); + tmpkey = make_new_set(&PyFrozenSet_Type, key); if (tmpkey == NULL) return NULL; - set_swap_bodies((PySetObject *)tmpkey, (PySetObject *)key); result = set_discard(so, tmpkey); - set_swap_bodies((PySetObject *)tmpkey, (PySetObject *)key); Py_DECREF(tmpkey); return result; } From python-checkins at python.org Fri Aug 6 12:43:31 2010 From: python-checkins at python.org (martin.v.loewis) Date: Fri, 6 Aug 2010 12:43:31 +0200 (CEST) Subject: [Python-checkins] r83758 - python/branches/py3k/Tools/msi/msilib.py Message-ID: <20100806104331.7F395F5AD@mail.python.org> Author: martin.v.loewis Date: Fri Aug 6 12:43:31 2010 New Revision: 83758 Log: Change pyc removal to support __pycache__. Modified: python/branches/py3k/Tools/msi/msilib.py Modified: python/branches/py3k/Tools/msi/msilib.py ============================================================================== --- python/branches/py3k/Tools/msi/msilib.py (original) +++ python/branches/py3k/Tools/msi/msilib.py Fri Aug 6 12:43:31 2010 @@ -583,10 +583,17 @@ return files def remove_pyc(self): - "Remove .pyc/.pyo files on uninstall" + "Remove .pyc/.pyo files from __pycache__ on uninstall" + directory = self.logical + "_pycache" + add_data(self.db, "Directory", [(directory, self.logical, "__PYCA~1|__pycache__")]) + flags = 256 if Win64 else 0 + add_data(self.db, "Component", + [(directory, gen_uuid(), directory, flags, None, None)]) + add_data(self.db, "FeatureComponents", [(current_feature.id, directory)]) + add_data(self.db, "CreateFolder", [(directory, directory)]) add_data(self.db, "RemoveFile", - [(self.component+"c", self.component, "*.pyc", self.logical, 2), - (self.component+"o", self.component, "*.pyo", self.logical, 2)]) + [(self.component, self.component, "*.*", directory, 2), + ]) def removefile(self, key, pattern): "Add a RemoveFile entry" From python-checkins at python.org Fri Aug 6 15:03:56 2010 From: python-checkins at python.org (tim.golden) Date: Fri, 6 Aug 2010 15:03:56 +0200 (CEST) Subject: [Python-checkins] r83759 - in python/branches/py3k: Lib/test/test_subprocess.py PC/_subprocess.c Message-ID: <20100806130356.9BE64EE981@mail.python.org> Author: tim.golden Date: Fri Aug 6 15:03:56 2010 New Revision: 83759 Log: Issue #3210: Ensure stdio handles are closed if CreateProcess fails Modified: python/branches/py3k/Lib/test/test_subprocess.py python/branches/py3k/PC/_subprocess.c Modified: python/branches/py3k/Lib/test/test_subprocess.py ============================================================================== --- python/branches/py3k/Lib/test/test_subprocess.py (original) +++ python/branches/py3k/Lib/test/test_subprocess.py Fri Aug 6 15:03:56 2010 @@ -544,6 +544,26 @@ output = subprocess.check_output([sys.executable, '-c', code]) self.assert_(output.startswith(b'Hello World!'), ascii(output)) + def test_handles_closed_on_exception(self): + # If CreateProcess exits with an error, ensure the + # duplicate output handles are released + ifhandle, ifname = mkstemp() + ofhandle, ofname = mkstemp() + efhandle, efname = mkstemp() + try: + subprocess.Popen (["*"], stdin=ifhandle, stdout=ofhandle, + stderr=efhandle) + except OSError: + os.close(ifhandle) + os.remove(ifname) + os.close(ofhandle) + os.remove(ofname) + os.close(efhandle) + os.remove(efname) + self.assertFalse(os.path.exists(ifname)) + self.assertFalse(os.path.exists(ofname)) + self.assertFalse(os.path.exists(efname)) + # context manager class _SuppressCoreFiles(object): Modified: python/branches/py3k/PC/_subprocess.c ============================================================================== --- python/branches/py3k/PC/_subprocess.c (original) +++ python/branches/py3k/PC/_subprocess.c Fri Aug 6 15:03:56 2010 @@ -429,6 +429,7 @@ PyObject* env_mapping; Py_UNICODE* current_directory; PyObject* startup_info; + DWORD error; if (! PyArg_ParseTuple(args, "ZZOOiiOZO:CreateProcess", &application_name, @@ -478,8 +479,22 @@ Py_XDECREF(environment); - if (! result) - return PyErr_SetFromWindowsErr(GetLastError()); + if (! result) { + error = GetLastError(); + if(si.hStdInput != INVALID_HANDLE_VALUE) { + CloseHandle(si.hStdInput); + si.hStdInput = INVALID_HANDLE_VALUE; + } + if(si.hStdOutput != INVALID_HANDLE_VALUE) { + CloseHandle(si.hStdOutput); + si.hStdOutput = INVALID_HANDLE_VALUE; + } + if(si.hStdError != INVALID_HANDLE_VALUE) { + CloseHandle(si.hStdError); + si.hStdError = INVALID_HANDLE_VALUE; + } + return PyErr_SetFromWindowsErr(error); + } return Py_BuildValue("NNii", sp_handle_new(pi.hProcess), From python-checkins at python.org Fri Aug 6 15:14:33 2010 From: python-checkins at python.org (tim.golden) Date: Fri, 6 Aug 2010 15:14:33 +0200 (CEST) Subject: [Python-checkins] r83760 - in python/branches/release27-maint: Lib/test/test_subprocess.py PC/_subprocess.c Message-ID: <20100806131433.2A254EE9CF@mail.python.org> Author: tim.golden Date: Fri Aug 6 15:14:33 2010 New Revision: 83760 Log: Issue #3210: Ensure stdio handles are closed if CreateProcess fails Modified: python/branches/release27-maint/Lib/test/test_subprocess.py python/branches/release27-maint/PC/_subprocess.c Modified: python/branches/release27-maint/Lib/test/test_subprocess.py ============================================================================== --- python/branches/release27-maint/Lib/test/test_subprocess.py (original) +++ python/branches/release27-maint/Lib/test/test_subprocess.py Fri Aug 6 15:14:33 2010 @@ -533,6 +533,26 @@ if c.exception.errno != 2: # ignore "no such file" raise c.exception + def test_handles_closed_on_exception(self): + # If CreateProcess exits with an error, ensure the + # duplicate output handles are released + ifhandle, ifname = mkstemp() + ofhandle, ofname = mkstemp() + efhandle, efname = mkstemp() + try: + subprocess.Popen (["*"], stdin=ifhandle, stdout=ofhandle, + stderr=efhandle) + except OSError: + os.close(ifhandle) + os.remove(ifname) + os.close(ofhandle) + os.remove(ofname) + os.close(efhandle) + os.remove(efname) + self.assertFalse(os.path.exists(ifname)) + self.assertFalse(os.path.exists(ofname)) + self.assertFalse(os.path.exists(efname)) + # context manager class _SuppressCoreFiles(object): Modified: python/branches/release27-maint/PC/_subprocess.c ============================================================================== --- python/branches/release27-maint/PC/_subprocess.c (original) +++ python/branches/release27-maint/PC/_subprocess.c Fri Aug 6 15:14:33 2010 @@ -425,6 +425,7 @@ PyObject* env_mapping; char* current_directory; PyObject* startup_info; + DWORD error; if (! PyArg_ParseTuple(args, "zzOOiiOzO:CreateProcess", &application_name, @@ -474,8 +475,22 @@ Py_XDECREF(environment); - if (! result) - return PyErr_SetFromWindowsErr(GetLastError()); + if (! result) { + error = GetLastError(); + if(si.hStdInput != INVALID_HANDLE_VALUE) { + CloseHandle(si.hStdInput); + si.hStdInput = INVALID_HANDLE_VALUE; + } + if(si.hStdOutput != INVALID_HANDLE_VALUE) { + CloseHandle(si.hStdOutput); + si.hStdOutput = INVALID_HANDLE_VALUE; + } + if(si.hStdError != INVALID_HANDLE_VALUE) { + CloseHandle(si.hStdError); + si.hStdError = INVALID_HANDLE_VALUE; + } + return PyErr_SetFromWindowsErr(error); + } return Py_BuildValue("NNii", sp_handle_new(pi.hProcess), From python-checkins at python.org Fri Aug 6 15:20:12 2010 From: python-checkins at python.org (tim.golden) Date: Fri, 6 Aug 2010 15:20:12 +0200 (CEST) Subject: [Python-checkins] r83761 - in python/branches/release31-maint: Lib/test/test_subprocess.py PC/_subprocess.c Message-ID: <20100806132012.3844DD36E@mail.python.org> Author: tim.golden Date: Fri Aug 6 15:20:12 2010 New Revision: 83761 Log: Issue #3210: Ensure stdio handles are closed if CreateProcess fails Modified: python/branches/release31-maint/Lib/test/test_subprocess.py python/branches/release31-maint/PC/_subprocess.c Modified: python/branches/release31-maint/Lib/test/test_subprocess.py ============================================================================== --- python/branches/release31-maint/Lib/test/test_subprocess.py (original) +++ python/branches/release31-maint/Lib/test/test_subprocess.py Fri Aug 6 15:20:12 2010 @@ -548,6 +548,26 @@ output = subprocess.check_output([sys.executable, '-c', code]) self.assert_(output.startswith(b'Hello World!'), ascii(output)) + def test_handles_closed_on_exception(self): + # If CreateProcess exits with an error, ensure the + # duplicate output handles are released + ifhandle, ifname = self.mkstemp() + ofhandle, ofname = self.mkstemp() + efhandle, efname = self.mkstemp() + try: + subprocess.Popen (["*"], stdin=ifhandle, stdout=ofhandle, + stderr=efhandle) + except OSError: + os.close(ifhandle) + os.remove(ifname) + os.close(ofhandle) + os.remove(ofname) + os.close(efhandle) + os.remove(efname) + self.assertFalse(os.path.exists(ifname)) + self.assertFalse(os.path.exists(ofname)) + self.assertFalse(os.path.exists(efname)) + # # POSIX tests # Modified: python/branches/release31-maint/PC/_subprocess.c ============================================================================== --- python/branches/release31-maint/PC/_subprocess.c (original) +++ python/branches/release31-maint/PC/_subprocess.c Fri Aug 6 15:20:12 2010 @@ -429,6 +429,7 @@ PyObject* env_mapping; Py_UNICODE* current_directory; PyObject* startup_info; + DWORD error; if (! PyArg_ParseTuple(args, "ZZOOiiOZO:CreateProcess", &application_name, @@ -478,8 +479,22 @@ Py_XDECREF(environment); - if (! result) - return PyErr_SetFromWindowsErr(GetLastError()); + if (! result) { + error = GetLastError(); + if(si.hStdInput != INVALID_HANDLE_VALUE) { + CloseHandle(si.hStdInput); + si.hStdInput = INVALID_HANDLE_VALUE; + } + if(si.hStdOutput != INVALID_HANDLE_VALUE) { + CloseHandle(si.hStdOutput); + si.hStdOutput = INVALID_HANDLE_VALUE; + } + if(si.hStdError != INVALID_HANDLE_VALUE) { + CloseHandle(si.hStdError); + si.hStdError = INVALID_HANDLE_VALUE; + } + return PyErr_SetFromWindowsErr(error); + } return Py_BuildValue("NNii", sp_handle_new(pi.hProcess), From python-checkins at python.org Fri Aug 6 20:55:30 2010 From: python-checkins at python.org (mark.dickinson) Date: Fri, 6 Aug 2010 20:55:30 +0200 (CEST) Subject: [Python-checkins] r83762 - in python/branches/py3k: Misc/NEWS Objects/sliceobject.c Message-ID: <20100806185530.5F088EE9DA@mail.python.org> Author: mark.dickinson Date: Fri Aug 6 20:55:26 2010 New Revision: 83762 Log: In PySlice_IndicesEx, clip the step to [-PY_SSIZE_T_MAX, PY_SSIZE_T_MAX] rather than [PY_SSIZE_T_MIN, PY_SSIZE_T_MAX]. Modified: python/branches/py3k/Misc/NEWS python/branches/py3k/Objects/sliceobject.c Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Fri Aug 6 20:55:26 2010 @@ -529,6 +529,10 @@ C-API ----- +- PySlice_GetIndicesEx now clips the step to [-PY_SSIZE_T_MAX, PY_SSIZE_T_MAX] + instead of [-PY_SSIZE_T_MAX-1, PY_SSIZE_T_MAX]. This makes it safe to do + "step = -step" when reversing a slice. + - Issue #5753: A new C API function, `PySys_SetArgvEx`, allows embedders of the interpreter to set sys.argv without also modifying sys.path. This helps fix `CVE-2008-5983 Modified: python/branches/py3k/Objects/sliceobject.c ============================================================================== --- python/branches/py3k/Objects/sliceobject.c (original) +++ python/branches/py3k/Objects/sliceobject.c Fri Aug 6 20:55:26 2010 @@ -131,7 +131,8 @@ int PySlice_GetIndicesEx(PySliceObject *r, Py_ssize_t length, - Py_ssize_t *start, Py_ssize_t *stop, Py_ssize_t *step, Py_ssize_t *slicelength) + Py_ssize_t *start, Py_ssize_t *stop, Py_ssize_t *step, + Py_ssize_t *slicelength) { /* this is harder to get right than you might think */ @@ -147,6 +148,13 @@ "slice step cannot be zero"); return -1; } + /* Here *step might be -PY_SSIZE_T_MAX-1; in this case we replace it + * with -PY_SSIZE_T_MAX. This doesn't affect the semantics, and it + * guards against later undefined behaviour resulting from code that + * does "step = -step" as part of a slice reversal. + */ + if (*step < -PY_SSIZE_T_MAX) + *step = -PY_SSIZE_T_MAX; } defstart = *step < 0 ? length-1 : 0; From python-checkins at python.org Fri Aug 6 21:27:32 2010 From: python-checkins at python.org (brian.curtin) Date: Fri, 6 Aug 2010 21:27:32 +0200 (CEST) Subject: [Python-checkins] r83763 - in python/branches/py3k: Doc/library/signal.rst Lib/test/test_signal.py Misc/NEWS Modules/signalmodule.c Message-ID: <20100806192732.5E323EE9D1@mail.python.org> Author: brian.curtin Date: Fri Aug 6 21:27:32 2010 New Revision: 83763 Log: Fix #9324: Add parameter validation to signal.signal on Windows in order to prevent crashes. Modified: python/branches/py3k/Doc/library/signal.rst python/branches/py3k/Lib/test/test_signal.py python/branches/py3k/Misc/NEWS python/branches/py3k/Modules/signalmodule.c Modified: python/branches/py3k/Doc/library/signal.rst ============================================================================== --- python/branches/py3k/Doc/library/signal.rst (original) +++ python/branches/py3k/Doc/library/signal.rst Fri Aug 6 21:27:32 2010 @@ -230,6 +230,10 @@ see the :ref:`description in the type hierarchy ` or see the attribute descriptions in the :mod:`inspect` module). + On Windows, :func:`signal` can only be called with :const:`SIGABRT`, + :const:`SIGFPE`, :const:`SIGILL`, :const:`SIGINT`, :const:`SIGSEGV`, or + :const:`SIGTERM`. A :exc:`ValueError` will be raised in any other case. + .. _signal-example: Modified: python/branches/py3k/Lib/test/test_signal.py ============================================================================== --- python/branches/py3k/Lib/test/test_signal.py (original) +++ python/branches/py3k/Lib/test/test_signal.py Fri Aug 6 21:27:32 2010 @@ -9,7 +9,7 @@ import traceback import sys, os, time, errno -if sys.platform[:3] in ('win', 'os2') or sys.platform == 'riscos': +if sys.platform == 'os2' or sys.platform == 'riscos': raise unittest.SkipTest("Can't test signal on %s" % \ sys.platform) @@ -37,6 +37,7 @@ return None + at unittest.skipIf(sys.platform == "win32", "Not valid on Windows") class InterProcessSignalTests(unittest.TestCase): MAX_DURATION = 20 # Entire test should last at most 20 sec. @@ -186,6 +187,7 @@ self.MAX_DURATION) + at unittest.skipIf(sys.platform == "win32", "Not valid on Windows") class BasicSignalTests(unittest.TestCase): def trivial_signal_handler(self, *args): pass @@ -208,6 +210,23 @@ self.assertEquals(signal.getsignal(signal.SIGHUP), hup) + at unittest.skipUnless(sys.platform == "win32", "Windows specific") +class WindowsSignalTests(unittest.TestCase): + def test_issue9324(self): + handler = lambda x, y: None + signal.signal(signal.SIGABRT, handler) + signal.signal(signal.SIGFPE, handler) + signal.signal(signal.SIGILL, handler) + signal.signal(signal.SIGINT, handler) + signal.signal(signal.SIGSEGV, handler) + signal.signal(signal.SIGTERM, handler) + + with self.assertRaises(ValueError): + signal.signal(-1, handler) + sinal.signal(7, handler) + + + at unittest.skipIf(sys.platform == "win32", "Not valid on Windows") class WakeupSignalTests(unittest.TestCase): TIMEOUT_FULL = 10 TIMEOUT_HALF = 5 @@ -253,14 +272,15 @@ os.close(self.write) signal.signal(signal.SIGALRM, self.alrm) + at unittest.skipIf(sys.platform == "win32", "Not valid on Windows") class SiginterruptTest(unittest.TestCase): - signum = signal.SIGUSR1 def setUp(self): """Install a no-op signal handler that can be set to allow interrupts or not, and arrange for the original signal handler to be re-installed when the test is finished. """ + self.signum = signal.SIGUSR1 oldhandler = signal.signal(self.signum, lambda x,y: None) self.addCleanup(signal.signal, self.signum, oldhandler) @@ -354,7 +374,7 @@ self.assertFalse(i) - + at unittest.skipIf(sys.platform == "win32", "Not valid on Windows") class ItimerTest(unittest.TestCase): def setUp(self): self.hndl_called = False @@ -463,8 +483,11 @@ self.assertEqual(self.hndl_called, True) def test_main(): - support.run_unittest(BasicSignalTests, InterProcessSignalTests, - WakeupSignalTests, SiginterruptTest, ItimerTest) + if sys.platform == "win32": + support.run_unittest(WindowsSignalTests) + else: + support.run_unittest(BasicSignalTests, InterProcessSignalTests, + WakeupSignalTests, SiginterruptTest, ItimerTest) if __name__ == "__main__": Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Fri Aug 6 21:27:32 2010 @@ -24,6 +24,9 @@ Extensions ---------- +- Issue #9324: Add parameter validation to signal.signal on Windows in order + to prevent crashes. + - Issue #9526: Remove some outdated (int) casts that were preventing the array module from working correctly with arrays of more than 2**31 elements. Modified: python/branches/py3k/Modules/signalmodule.c ============================================================================== --- python/branches/py3k/Modules/signalmodule.c (original) +++ python/branches/py3k/Modules/signalmodule.c Fri Aug 6 21:27:32 2010 @@ -255,8 +255,23 @@ int sig_num; PyObject *old_handler; void (*func)(int); +#ifdef MS_WINDOWS + int cur_sig, num_valid_sigs = 6; + static int valid_sigs[] = {SIGABRT, SIGFPE, SIGILL, SIGINT, + SIGSEGV, SIGTERM}; + BOOL valid_sig = FALSE; +#endif if (!PyArg_ParseTuple(args, "iO:signal", &sig_num, &obj)) return NULL; +#ifdef MS_WINDOWS + /* Validate that sig_num is one of the allowable signals */ + for (cur_sig = 0; cur_sig < num_valid_sigs; cur_sig++) + valid_sig |= (sig_num == valid_sigs[cur_sig]); + if (!valid_sig) { + PyErr_SetString(PyExc_ValueError, "signal number out of range"); + return NULL; + } +#endif #ifdef WITH_THREAD if (PyThread_get_thread_ident() != main_thread) { PyErr_SetString(PyExc_ValueError, From python-checkins at python.org Fri Aug 6 21:34:52 2010 From: python-checkins at python.org (brian.curtin) Date: Fri, 6 Aug 2010 21:34:52 +0200 (CEST) Subject: [Python-checkins] r83764 - in python/branches/release31-maint: Doc/library/signal.rst Lib/test/test_signal.py Misc/NEWS Modules/signalmodule.c Message-ID: <20100806193452.925CEEE9AB@mail.python.org> Author: brian.curtin Date: Fri Aug 6 21:34:52 2010 New Revision: 83764 Log: Merged revisions 83763 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83763 | brian.curtin | 2010-08-06 14:27:32 -0500 (Fri, 06 Aug 2010) | 3 lines Fix #9324: Add parameter validation to signal.signal on Windows in order to prevent crashes. ........ Modified: python/branches/release31-maint/Doc/library/signal.rst python/branches/release31-maint/Lib/test/test_signal.py python/branches/release31-maint/Misc/NEWS python/branches/release31-maint/Modules/signalmodule.c Modified: python/branches/release31-maint/Doc/library/signal.rst ============================================================================== --- python/branches/release31-maint/Doc/library/signal.rst (original) +++ python/branches/release31-maint/Doc/library/signal.rst Fri Aug 6 21:34:52 2010 @@ -210,6 +210,10 @@ see the :ref:`description in the type hierarchy ` or see the attribute descriptions in the :mod:`inspect` module). + On Windows, :func:`signal` can only be called with :const:`SIGABRT`, + :const:`SIGFPE`, :const:`SIGILL`, :const:`SIGINT`, :const:`SIGSEGV`, or + :const:`SIGTERM`. A :exc:`ValueError` will be raised in any other case. + .. _signal-example: Modified: python/branches/release31-maint/Lib/test/test_signal.py ============================================================================== --- python/branches/release31-maint/Lib/test/test_signal.py (original) +++ python/branches/release31-maint/Lib/test/test_signal.py Fri Aug 6 21:34:52 2010 @@ -9,7 +9,7 @@ import traceback import sys, os, time, errno -if sys.platform[:3] in ('win', 'os2') or sys.platform == 'riscos': +if sys.platform == 'os2' or sys.platform == 'riscos': raise unittest.SkipTest("Can't test signal on %s" % \ sys.platform) @@ -37,6 +37,7 @@ return None + at unittest.skipIf(sys.platform == "win32", "Not valid on Windows") class InterProcessSignalTests(unittest.TestCase): MAX_DURATION = 20 # Entire test should last at most 20 sec. @@ -186,6 +187,7 @@ self.MAX_DURATION) + at unittest.skipIf(sys.platform == "win32", "Not valid on Windows") class BasicSignalTests(unittest.TestCase): def trivial_signal_handler(self, *args): pass @@ -208,6 +210,23 @@ self.assertEquals(signal.getsignal(signal.SIGHUP), hup) + at unittest.skipUnless(sys.platform == "win32", "Windows specific") +class WindowsSignalTests(unittest.TestCase): + def test_issue9324(self): + handler = lambda x, y: None + signal.signal(signal.SIGABRT, handler) + signal.signal(signal.SIGFPE, handler) + signal.signal(signal.SIGILL, handler) + signal.signal(signal.SIGINT, handler) + signal.signal(signal.SIGSEGV, handler) + signal.signal(signal.SIGTERM, handler) + + with self.assertRaises(ValueError): + signal.signal(-1, handler) + sinal.signal(7, handler) + + + at unittest.skipIf(sys.platform == "win32", "Not valid on Windows") class WakeupSignalTests(unittest.TestCase): TIMEOUT_FULL = 10 TIMEOUT_HALF = 5 @@ -253,14 +272,15 @@ os.close(self.write) signal.signal(signal.SIGALRM, self.alrm) + at unittest.skipIf(sys.platform == "win32", "Not valid on Windows") class SiginterruptTest(unittest.TestCase): - signum = signal.SIGUSR1 def setUp(self): """Install a no-op signal handler that can be set to allow interrupts or not, and arrange for the original signal handler to be re-installed when the test is finished. """ + self.signum = signal.SIGUSR1 oldhandler = signal.signal(self.signum, lambda x,y: None) self.addCleanup(signal.signal, self.signum, oldhandler) @@ -354,7 +374,7 @@ self.assertFalse(i) - + at unittest.skipIf(sys.platform == "win32", "Not valid on Windows") class ItimerTest(unittest.TestCase): def setUp(self): self.hndl_called = False @@ -463,8 +483,11 @@ self.assertEqual(self.hndl_called, True) def test_main(): - support.run_unittest(BasicSignalTests, InterProcessSignalTests, - WakeupSignalTests, SiginterruptTest, ItimerTest) + if sys.platform == "win32": + support.run_unittest(WindowsSignalTests) + else: + support.run_unittest(BasicSignalTests, InterProcessSignalTests, + WakeupSignalTests, SiginterruptTest, ItimerTest) if __name__ == "__main__": Modified: python/branches/release31-maint/Misc/NEWS ============================================================================== --- python/branches/release31-maint/Misc/NEWS (original) +++ python/branches/release31-maint/Misc/NEWS Fri Aug 6 21:34:52 2010 @@ -394,6 +394,9 @@ Extension Modules ----------------- +- Issue #9324: Add parameter validation to signal.signal on Windows in order + to prevent crashes. + - Issue #9526: Remove some outdated (int) casts that were preventing the array module from working correctly with arrays of more than 2**31 elements. Modified: python/branches/release31-maint/Modules/signalmodule.c ============================================================================== --- python/branches/release31-maint/Modules/signalmodule.c (original) +++ python/branches/release31-maint/Modules/signalmodule.c Fri Aug 6 21:34:52 2010 @@ -249,8 +249,23 @@ int sig_num; PyObject *old_handler; void (*func)(int); +#ifdef MS_WINDOWS + int cur_sig, num_valid_sigs = 6; + static int valid_sigs[] = {SIGABRT, SIGFPE, SIGILL, SIGINT, + SIGSEGV, SIGTERM}; + BOOL valid_sig = FALSE; +#endif if (!PyArg_ParseTuple(args, "iO:signal", &sig_num, &obj)) return NULL; +#ifdef MS_WINDOWS + /* Validate that sig_num is one of the allowable signals */ + for (cur_sig = 0; cur_sig < num_valid_sigs; cur_sig++) + valid_sig |= (sig_num == valid_sigs[cur_sig]); + if (!valid_sig) { + PyErr_SetString(PyExc_ValueError, "signal number out of range"); + return NULL; + } +#endif #ifdef WITH_THREAD if (PyThread_get_thread_ident() != main_thread) { PyErr_SetString(PyExc_ValueError, From python-checkins at python.org Fri Aug 6 21:41:02 2010 From: python-checkins at python.org (brian.curtin) Date: Fri, 6 Aug 2010 21:41:02 +0200 (CEST) Subject: [Python-checkins] r83765 - in python/branches/release27-maint: Doc/library/signal.rst Lib/test/test_signal.py Misc/NEWS Modules/signalmodule.c Message-ID: <20100806194102.3783CF6A1@mail.python.org> Author: brian.curtin Date: Fri Aug 6 21:41:01 2010 New Revision: 83765 Log: Merged revisions 83763 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83763 | brian.curtin | 2010-08-06 14:27:32 -0500 (Fri, 06 Aug 2010) | 3 lines Fix #9324: Add parameter validation to signal.signal on Windows in order to prevent crashes. ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Doc/library/signal.rst python/branches/release27-maint/Lib/test/test_signal.py python/branches/release27-maint/Misc/NEWS python/branches/release27-maint/Modules/signalmodule.c Modified: python/branches/release27-maint/Doc/library/signal.rst ============================================================================== --- python/branches/release27-maint/Doc/library/signal.rst (original) +++ python/branches/release27-maint/Doc/library/signal.rst Fri Aug 6 21:41:01 2010 @@ -236,6 +236,10 @@ see the :ref:`description in the type hierarchy ` or see the attribute descriptions in the :mod:`inspect` module). + On Windows, :func:`signal` can only be called with :const:`SIGABRT`, + :const:`SIGFPE`, :const:`SIGILL`, :const:`SIGINT`, :const:`SIGSEGV`, or + :const:`SIGTERM`. A :exc:`ValueError` will be raised in any other case. + .. _signal-example: Modified: python/branches/release27-maint/Lib/test/test_signal.py ============================================================================== --- python/branches/release27-maint/Lib/test/test_signal.py (original) +++ python/branches/release27-maint/Lib/test/test_signal.py Fri Aug 6 21:41:01 2010 @@ -9,7 +9,7 @@ import traceback import sys, os, time, errno -if sys.platform[:3] in ('win', 'os2') or sys.platform == 'riscos': +if sys.platform == 'os2' or sys.platform == 'riscos': raise unittest.SkipTest("Can't test signal on %s" % \ sys.platform) @@ -37,6 +37,7 @@ return None + at unittest.skipIf(sys.platform == "win32", "Not valid on Windows") class InterProcessSignalTests(unittest.TestCase): MAX_DURATION = 20 # Entire test should last at most 20 sec. @@ -186,6 +187,7 @@ self.MAX_DURATION) + at unittest.skipIf(sys.platform == "win32", "Not valid on Windows") class BasicSignalTests(unittest.TestCase): def trivial_signal_handler(self, *args): pass @@ -208,6 +210,23 @@ self.assertEquals(signal.getsignal(signal.SIGHUP), hup) + at unittest.skipUnless(sys.platform == "win32", "Windows specific") +class WindowsSignalTests(unittest.TestCase): + def test_issue9324(self): + handler = lambda x, y: None + signal.signal(signal.SIGABRT, handler) + signal.signal(signal.SIGFPE, handler) + signal.signal(signal.SIGILL, handler) + signal.signal(signal.SIGINT, handler) + signal.signal(signal.SIGSEGV, handler) + signal.signal(signal.SIGTERM, handler) + + with self.assertRaises(ValueError): + signal.signal(-1, handler) + sinal.signal(7, handler) + + + at unittest.skipIf(sys.platform == "win32", "Not valid on Windows") class WakeupSignalTests(unittest.TestCase): TIMEOUT_FULL = 10 TIMEOUT_HALF = 5 @@ -253,14 +272,15 @@ os.close(self.write) signal.signal(signal.SIGALRM, self.alrm) + at unittest.skipIf(sys.platform == "win32", "Not valid on Windows") class SiginterruptTest(unittest.TestCase): - signum = signal.SIGUSR1 def setUp(self): """Install a no-op signal handler that can be set to allow interrupts or not, and arrange for the original signal handler to be re-installed when the test is finished. """ + self.signum = signal.SIGUSR1 oldhandler = signal.signal(self.signum, lambda x,y: None) self.addCleanup(signal.signal, self.signum, oldhandler) @@ -354,7 +374,7 @@ self.assertFalse(i) - + at unittest.skipIf(sys.platform == "win32", "Not valid on Windows") class ItimerTest(unittest.TestCase): def setUp(self): self.hndl_called = False @@ -463,8 +483,11 @@ self.assertEqual(self.hndl_called, True) def test_main(): - test_support.run_unittest(BasicSignalTests, InterProcessSignalTests, - WakeupSignalTests, SiginterruptTest, ItimerTest) + if sys.platform == "win32": + support.run_unittest(WindowsSignalTests) + else: + support.run_unittest(BasicSignalTests, InterProcessSignalTests, + WakeupSignalTests, SiginterruptTest, ItimerTest) if __name__ == "__main__": Modified: python/branches/release27-maint/Misc/NEWS ============================================================================== --- python/branches/release27-maint/Misc/NEWS (original) +++ python/branches/release27-maint/Misc/NEWS Fri Aug 6 21:41:01 2010 @@ -144,6 +144,9 @@ Extension Modules ----------------- +- Issue #9324: Add parameter validation to signal.signal on Windows in order + to prevent crashes. + - Issue #9526: Remove some outdated (int) casts that were preventing the array module from working correctly with arrays of more than 2**31 elements. Modified: python/branches/release27-maint/Modules/signalmodule.c ============================================================================== --- python/branches/release27-maint/Modules/signalmodule.c (original) +++ python/branches/release27-maint/Modules/signalmodule.c Fri Aug 6 21:41:01 2010 @@ -255,8 +255,23 @@ int sig_num; PyObject *old_handler; void (*func)(int); +#ifdef MS_WINDOWS + int cur_sig, num_valid_sigs = 6; + static int valid_sigs[] = {SIGABRT, SIGFPE, SIGILL, SIGINT, + SIGSEGV, SIGTERM}; + BOOL valid_sig = FALSE; +#endif if (!PyArg_ParseTuple(args, "iO:signal", &sig_num, &obj)) return NULL; +#ifdef MS_WINDOWS + /* Validate that sig_num is one of the allowable signals */ + for (cur_sig = 0; cur_sig < num_valid_sigs; cur_sig++) + valid_sig |= (sig_num == valid_sigs[cur_sig]); + if (!valid_sig) { + PyErr_SetString(PyExc_ValueError, "signal number out of range"); + return NULL; + } +#endif #ifdef WITH_THREAD if (PyThread_get_thread_ident() != main_thread) { PyErr_SetString(PyExc_ValueError, From python-checkins at python.org Fri Aug 6 21:52:50 2010 From: python-checkins at python.org (brian.curtin) Date: Fri, 6 Aug 2010 21:52:50 +0200 (CEST) Subject: [Python-checkins] r83766 - python/branches/release31-maint/Modules/signalmodule.c Message-ID: <20100806195250.C363BEE981@mail.python.org> Author: brian.curtin Date: Fri Aug 6 21:52:50 2010 New Revision: 83766 Log: Include Windows.h for BOOL. 2.7 and 3.2 already had this due to a feature 3.1 doesn't have. Followup to #9324. Modified: python/branches/release31-maint/Modules/signalmodule.c Modified: python/branches/release31-maint/Modules/signalmodule.c ============================================================================== --- python/branches/release31-maint/Modules/signalmodule.c (original) +++ python/branches/release31-maint/Modules/signalmodule.c Fri Aug 6 21:52:50 2010 @@ -7,8 +7,11 @@ #include "intrcheck.h" #ifdef MS_WINDOWS +#include +#ifdef HAVE_PROCESS_H #include #endif +#endif #include From python-checkins at python.org Fri Aug 6 23:18:50 2010 From: python-checkins at python.org (r.david.murray) Date: Fri, 6 Aug 2010 23:18:50 +0200 (CEST) Subject: [Python-checkins] r83767 - python/branches/py3k/Misc/maintainers.rst Message-ID: <20100806211850.0990FEE9D1@mail.python.org> Author: r.david.murray Date: Fri Aug 6 23:18:49 2010 New Revision: 83767 Log: Add xml subpackages, since they are likely to have different maintainers. And if not they can be merged back together when that becomes apparent. Modified: python/branches/py3k/Misc/maintainers.rst Modified: python/branches/py3k/Misc/maintainers.rst ============================================================================== --- python/branches/py3k/Misc/maintainers.rst (original) +++ python/branches/py3k/Misc/maintainers.rst Fri Aug 6 23:18:49 2010 @@ -230,8 +230,15 @@ winsound effbot (inactive) wsgiref pje xdrlib -xml +xml.dom +xml.dom.minidom +xml.dom.pulldom xml.etree effbot (inactive) +xml.parsers.expat +xml.sax +xml.sax.handler +xml.sax.saxutils +xml.sax.xmlreader xmlrpc zipfile zipimport From python-checkins at python.org Fri Aug 6 23:33:18 2010 From: python-checkins at python.org (mark.dickinson) Date: Fri, 6 Aug 2010 23:33:18 +0200 (CEST) Subject: [Python-checkins] r83768 - python/branches/py3k/Objects/bytearrayobject.c Message-ID: <20100806213318.9B518EE9B9@mail.python.org> Author: mark.dickinson Date: Fri Aug 6 23:33:18 2010 New Revision: 83768 Log: Issue #9530: Fix a couple of places where undefined behaviour can occur, as a result of signed integer overflow. Modified: python/branches/py3k/Objects/bytearrayobject.c Modified: python/branches/py3k/Objects/bytearrayobject.c ============================================================================== --- python/branches/py3k/Objects/bytearrayobject.c (original) +++ python/branches/py3k/Objects/bytearrayobject.c Fri Aug 6 23:33:18 2010 @@ -649,6 +649,11 @@ if (!_canresize(self)) return -1; + + if (slicelen == 0) + /* Nothing to do here. */ + return 0; + if (step < 0) { stop = start + 1; start = stop + step * (slicelen - 1) - 1; @@ -665,7 +670,7 @@ self->ob_bytes + cur + 1, lim); } /* Move the tail of the bytes, in one chunk */ - cur = start + slicelen*step; + cur = start + (size_t)slicelen*step; if (cur < (size_t)PyByteArray_GET_SIZE(self)) { memmove(self->ob_bytes + cur - slicelen, self->ob_bytes + cur, @@ -679,7 +684,8 @@ } else { /* Assign slice */ - Py_ssize_t cur, i; + Py_ssize_t i; + size_t cur; if (needed != slicelen) { PyErr_Format(PyExc_ValueError, From python-checkins at python.org Fri Aug 6 23:49:50 2010 From: python-checkins at python.org (mark.dickinson) Date: Fri, 6 Aug 2010 23:49:50 +0200 (CEST) Subject: [Python-checkins] r83769 - python/branches/release27-maint/Lib/test/test_signal.py Message-ID: <20100806214950.79959EE9F5@mail.python.org> Author: mark.dickinson Date: Fri Aug 6 23:49:50 2010 New Revision: 83769 Log: Merge of r83763 introduced 'support' into test_signal; should be 'test_support' Modified: python/branches/release27-maint/Lib/test/test_signal.py Modified: python/branches/release27-maint/Lib/test/test_signal.py ============================================================================== --- python/branches/release27-maint/Lib/test/test_signal.py (original) +++ python/branches/release27-maint/Lib/test/test_signal.py Fri Aug 6 23:49:50 2010 @@ -484,9 +484,9 @@ def test_main(): if sys.platform == "win32": - support.run_unittest(WindowsSignalTests) + test_support.run_unittest(WindowsSignalTests) else: - support.run_unittest(BasicSignalTests, InterProcessSignalTests, + test_support.run_unittest(BasicSignalTests, InterProcessSignalTests, WakeupSignalTests, SiginterruptTest, ItimerTest) From python-checkins at python.org Sat Aug 7 01:23:49 2010 From: python-checkins at python.org (raymond.hettinger) Date: Sat, 7 Aug 2010 01:23:49 +0200 (CEST) Subject: [Python-checkins] r83770 - python/branches/py3k/Doc/whatsnew/3.2.rst Message-ID: <20100806232349.801BEEE982@mail.python.org> Author: raymond.hettinger Date: Sat Aug 7 01:23:49 2010 New Revision: 83770 Log: Improve the whatsnew article on the lru/lfu cache decorators. Modified: python/branches/py3k/Doc/whatsnew/3.2.rst Modified: python/branches/py3k/Doc/whatsnew/3.2.rst ============================================================================== --- python/branches/py3k/Doc/whatsnew/3.2.rst (original) +++ python/branches/py3k/Doc/whatsnew/3.2.rst Sat Aug 7 01:23:49 2010 @@ -71,8 +71,8 @@ save repeated queries to an external resource whenever the results are expected to be the same. - For example, adding an LFU decorator to a database query function can save - database accesses for the most popular searches:: + For example, adding a caching decorator to a database query function can save + database accesses for popular searches:: @functools.lfu_cache(maxsize=50) def get_phone_number(name): @@ -80,21 +80,32 @@ c.execute('SELECT phonenumber FROM phonelist WHERE name=?', (name,)) return c.fetchone()[0] - The LFU (least-frequently-used) cache gives best results when the distribution - of popular queries tends to remain the same over time. In contrast, the LRU - (least-recently-used) cache gives best results when the distribution changes - over time (for example, the most popular news articles change each day as - newer articles are added). + The caches support two strategies for limiting their size to *maxsize*. The + LFU (least-frequently-used) cache works bests when popular queries remain the + same over time. In contrast, the LRU (least-recently-used) cache works best + query popularity changes over time (for example, the most popular news + articles change each day as newer articles are added). - The two caching decorators can be composed (nested) to handle hybrid cases - that have both long-term access patterns and some short-term access trends. + The two caching decorators can be composed (nested) to handle hybrid cases. For example, music searches can reflect both long-term patterns (popular classics) and short-term trends (new releases):: - @functools.lfu_cache(maxsize=500) - @functools.lru_cache(maxsize=100) - def find_music(song): - ... + @functools.lfu_cache(maxsize=500) + @functools.lru_cache(maxsize=100) + def find_lyrics(song): + query = 'http://www.example.com/songlist/%s' % urllib.quote(song) + page = urllib.urlopen(query).read() + return parse_lyrics(page) + + To help with choosing an effective cache size, the wrapped function + is instrumented with two attributes 'hits' and 'misses':: + + >>> for song in user_requests: + ... find_lyrics(song) + >>> print find_lyrics.hits + 4805 + >>> print find_lyrics.misses + 980 (Contributed by Raymond Hettinger) From ezio.melotti at gmail.com Sat Aug 7 04:59:31 2010 From: ezio.melotti at gmail.com (Ezio Melotti) Date: Sat, 07 Aug 2010 05:59:31 +0300 Subject: [Python-checkins] r83763 - in python/branches/py3k: Doc/library/signal.rst Lib/test/test_signal.py Misc/NEWS Modules/signalmodule.c In-Reply-To: <20100806192732.5E323EE9D1@mail.python.org> References: <20100806192732.5E323EE9D1@mail.python.org> Message-ID: <4C5CCC13.5070709@gmail.com> Hi, On 06/08/2010 22.27, brian.curtin wrote: > Author: brian.curtin > Date: Fri Aug 6 21:27:32 2010 > New Revision: 83763 > > Log: > Fix #9324: Add parameter validation to signal.signal on Windows in order > to prevent crashes. > > > Modified: > python/branches/py3k/Doc/library/signal.rst > python/branches/py3k/Lib/test/test_signal.py > python/branches/py3k/Misc/NEWS > python/branches/py3k/Modules/signalmodule.c > > Modified: python/branches/py3k/Doc/library/signal.rst > ============================================================================== > --- python/branches/py3k/Doc/library/signal.rst (original) > +++ python/branches/py3k/Doc/library/signal.rst Fri Aug 6 21:27:32 2010 > @@ -230,6 +230,10 @@ > see the :ref:`description in the type hierarchy` or see the > attribute descriptions in the :mod:`inspect` module). > > + On Windows, :func:`signal` can only be called with :const:`SIGABRT`, > + :const:`SIGFPE`, :const:`SIGILL`, :const:`SIGINT`, :const:`SIGSEGV`, or > + :const:`SIGTERM`. A :exc:`ValueError` will be raised in any other case. > + > > .. _signal-example: > > > Modified: python/branches/py3k/Lib/test/test_signal.py > ============================================================================== > --- python/branches/py3k/Lib/test/test_signal.py (original) > +++ python/branches/py3k/Lib/test/test_signal.py Fri Aug 6 21:27:32 2010 > @@ -9,7 +9,7 @@ > import traceback > import sys, os, time, errno > > -if sys.platform[:3] in ('win', 'os2') or sys.platform == 'riscos': > +if sys.platform == 'os2' or sys.platform == 'riscos': > raise unittest.SkipTest("Can't test signal on %s" % \ > sys.platform) > > @@ -37,6 +37,7 @@ > return None > > > + at unittest.skipIf(sys.platform == "win32", "Not valid on Windows") In the previous chunk sys.platform[:3] was used instead of just sys.platform. Are there some other "winXX" platform that should be skipped too? > class InterProcessSignalTests(unittest.TestCase): > MAX_DURATION = 20 # Entire test should last at most 20 sec. > > @@ -186,6 +187,7 @@ > self.MAX_DURATION) > > > + at unittest.skipIf(sys.platform == "win32", "Not valid on Windows") > class BasicSignalTests(unittest.TestCase): > def trivial_signal_handler(self, *args): > pass > @@ -208,6 +210,23 @@ > self.assertEquals(signal.getsignal(signal.SIGHUP), hup) > > > + at unittest.skipUnless(sys.platform == "win32", "Windows specific") > +class WindowsSignalTests(unittest.TestCase): > + def test_issue9324(self): > + handler = lambda x, y: None > + signal.signal(signal.SIGABRT, handler) > + signal.signal(signal.SIGFPE, handler) > + signal.signal(signal.SIGILL, handler) > + signal.signal(signal.SIGINT, handler) > + signal.signal(signal.SIGSEGV, handler) > + signal.signal(signal.SIGTERM, handler) > + > + with self.assertRaises(ValueError): > + signal.signal(-1, handler) > + sinal.signal(7, handler) You should use two separate assertRaises here, otherwise the second line is not executed if the first one raises an error (and if the first doesn't raise an error but the second does the test will pass even if it shouldn't). This also masks the typo in the second line. > + > + > + at unittest.skipIf(sys.platform == "win32", "Not valid on Windows") > class WakeupSignalTests(unittest.TestCase): > TIMEOUT_FULL = 10 > TIMEOUT_HALF = 5 > @@ -253,14 +272,15 @@ > os.close(self.write) > signal.signal(signal.SIGALRM, self.alrm) > > + at unittest.skipIf(sys.platform == "win32", "Not valid on Windows") > class SiginterruptTest(unittest.TestCase): > - signum = signal.SIGUSR1 > > def setUp(self): > """Install a no-op signal handler that can be set to allow > interrupts or not, and arrange for the original signal handler to be > re-installed when the test is finished. > """ > + self.signum = signal.SIGUSR1 > oldhandler = signal.signal(self.signum, lambda x,y: None) > self.addCleanup(signal.signal, self.signum, oldhandler) > > @@ -354,7 +374,7 @@ > self.assertFalse(i) > > > - > + at unittest.skipIf(sys.platform == "win32", "Not valid on Windows") > class ItimerTest(unittest.TestCase): > def setUp(self): > self.hndl_called = False > @@ -463,8 +483,11 @@ > self.assertEqual(self.hndl_called, True) > > def test_main(): > - support.run_unittest(BasicSignalTests, InterProcessSignalTests, > - WakeupSignalTests, SiginterruptTest, ItimerTest) > + if sys.platform == "win32": > + support.run_unittest(WindowsSignalTests) > + else: > + support.run_unittest(BasicSignalTests, InterProcessSignalTests, > + WakeupSignalTests, SiginterruptTest, ItimerTest) Is this necessary? If the tests are marked with a skip decorator they will be skipped anyway (and also marked as skipped in the output). > > > if __name__ == "__main__": > > Modified: python/branches/py3k/Misc/NEWS > ============================================================================== > --- python/branches/py3k/Misc/NEWS (original) > +++ python/branches/py3k/Misc/NEWS Fri Aug 6 21:27:32 2010 > @@ -24,6 +24,9 @@ > Extensions > ---------- > > +- Issue #9324: Add parameter validation to signal.signal on Windows in order > + to prevent crashes. > + > - Issue #9526: Remove some outdated (int) casts that were preventing > the array module from working correctly with arrays of more than > 2**31 elements. > > Modified: python/branches/py3k/Modules/signalmodule.c > ============================================================================== > --- python/branches/py3k/Modules/signalmodule.c (original) > +++ python/branches/py3k/Modules/signalmodule.c Fri Aug 6 21:27:32 2010 > @@ -255,8 +255,23 @@ > int sig_num; > PyObject *old_handler; > void (*func)(int); > +#ifdef MS_WINDOWS > + int cur_sig, num_valid_sigs = 6; > + static int valid_sigs[] = {SIGABRT, SIGFPE, SIGILL, SIGINT, > + SIGSEGV, SIGTERM}; > + BOOL valid_sig = FALSE; > +#endif > if (!PyArg_ParseTuple(args, "iO:signal",&sig_num,&obj)) > return NULL; > +#ifdef MS_WINDOWS > + /* Validate that sig_num is one of the allowable signals */ > + for (cur_sig = 0; cur_sig< num_valid_sigs; cur_sig++) > + valid_sig |= (sig_num == valid_sigs[cur_sig]); > + if (!valid_sig) { > + PyErr_SetString(PyExc_ValueError, "signal number out of range"); > + return NULL; > + } > +#endif > #ifdef WITH_THREAD > if (PyThread_get_thread_ident() != main_thread) { > PyErr_SetString(PyExc_ValueError, > _______________________________________________ > Python-checkins mailing list > Python-checkins at python.org > http://mail.python.org/mailman/listinfo/python-checkins > Best Regards, Ezio Melotti From solipsis at pitrou.net Sat Aug 7 05:07:20 2010 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Sat, 07 Aug 2010 05:07:20 +0200 Subject: [Python-checkins] Daily py3k reference leaks (r83770): sum=0 Message-ID: py3k results for svn r83770 (hg cset 533abc944b32) -------------------------------------------------- Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/py3k/refleaks/reflogj3Dt7d', '-x'] From python-checkins at python.org Sat Aug 7 05:47:21 2010 From: python-checkins at python.org (brian.curtin) Date: Sat, 7 Aug 2010 05:47:21 +0200 (CEST) Subject: [Python-checkins] r83771 - python/branches/py3k/Lib/test/test_signal.py Message-ID: <20100807034721.59E48EEA39@mail.python.org> Author: brian.curtin Date: Sat Aug 7 05:47:21 2010 New Revision: 83771 Log: Fix an assertRaises situation and typo. Also pass all tests to run_unittest rather than do it by platform -- the proper skips are in place already. Modified: python/branches/py3k/Lib/test/test_signal.py Modified: python/branches/py3k/Lib/test/test_signal.py ============================================================================== --- python/branches/py3k/Lib/test/test_signal.py (original) +++ python/branches/py3k/Lib/test/test_signal.py Sat Aug 7 05:47:21 2010 @@ -223,7 +223,9 @@ with self.assertRaises(ValueError): signal.signal(-1, handler) - sinal.signal(7, handler) + + with self.assertRaises(ValueError): + signal.signal(7, handler) @unittest.skipIf(sys.platform == "win32", "Not valid on Windows") @@ -483,11 +485,9 @@ self.assertEqual(self.hndl_called, True) def test_main(): - if sys.platform == "win32": - support.run_unittest(WindowsSignalTests) - else: - support.run_unittest(BasicSignalTests, InterProcessSignalTests, - WakeupSignalTests, SiginterruptTest, ItimerTest) + support.run_unittest(BasicSignalTests, InterProcessSignalTests, + WakeupSignalTests, SiginterruptTest, + ItimerTest, WindowsSignalTests) if __name__ == "__main__": From python-checkins at python.org Sat Aug 7 05:52:38 2010 From: python-checkins at python.org (brian.curtin) Date: Sat, 7 Aug 2010 05:52:38 +0200 (CEST) Subject: [Python-checkins] r83772 - in python/branches/release31-maint: Lib/test/test_signal.py Message-ID: <20100807035238.54237EE992@mail.python.org> Author: brian.curtin Date: Sat Aug 7 05:52:38 2010 New Revision: 83772 Log: Merged revisions 83771 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83771 | brian.curtin | 2010-08-06 22:47:21 -0500 (Fri, 06 Aug 2010) | 3 lines Fix an assertRaises situation and typo. Also pass all tests to run_unittest rather than do it by platform -- the proper skips are in place already. ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Lib/test/test_signal.py Modified: python/branches/release31-maint/Lib/test/test_signal.py ============================================================================== --- python/branches/release31-maint/Lib/test/test_signal.py (original) +++ python/branches/release31-maint/Lib/test/test_signal.py Sat Aug 7 05:52:38 2010 @@ -223,7 +223,9 @@ with self.assertRaises(ValueError): signal.signal(-1, handler) - sinal.signal(7, handler) + + with self.assertRaises(ValueError): + signal.signal(7, handler) @unittest.skipIf(sys.platform == "win32", "Not valid on Windows") @@ -483,11 +485,9 @@ self.assertEqual(self.hndl_called, True) def test_main(): - if sys.platform == "win32": - support.run_unittest(WindowsSignalTests) - else: - support.run_unittest(BasicSignalTests, InterProcessSignalTests, - WakeupSignalTests, SiginterruptTest, ItimerTest) + support.run_unittest(BasicSignalTests, InterProcessSignalTests, + WakeupSignalTests, SiginterruptTest, + ItimerTest, WindowsSignalTests) if __name__ == "__main__": From python-checkins at python.org Sat Aug 7 05:56:16 2010 From: python-checkins at python.org (brian.curtin) Date: Sat, 7 Aug 2010 05:56:16 +0200 (CEST) Subject: [Python-checkins] r83773 - in python/branches/release27-maint: Lib/test/test_signal.py Message-ID: <20100807035616.DF8FBEE992@mail.python.org> Author: brian.curtin Date: Sat Aug 7 05:56:16 2010 New Revision: 83773 Log: Merged revisions 83771 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83771 | brian.curtin | 2010-08-06 22:47:21 -0500 (Fri, 06 Aug 2010) | 3 lines Fix an assertRaises situation and typo. Also pass all tests to run_unittest rather than do it by platform -- the proper skips are in place already. ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Lib/test/test_signal.py Modified: python/branches/release27-maint/Lib/test/test_signal.py ============================================================================== --- python/branches/release27-maint/Lib/test/test_signal.py (original) +++ python/branches/release27-maint/Lib/test/test_signal.py Sat Aug 7 05:56:16 2010 @@ -223,7 +223,9 @@ with self.assertRaises(ValueError): signal.signal(-1, handler) - sinal.signal(7, handler) + + with self.assertRaises(ValueError): + signal.signal(7, handler) @unittest.skipIf(sys.platform == "win32", "Not valid on Windows") @@ -483,11 +485,9 @@ self.assertEqual(self.hndl_called, True) def test_main(): - if sys.platform == "win32": - test_support.run_unittest(WindowsSignalTests) - else: - test_support.run_unittest(BasicSignalTests, InterProcessSignalTests, - WakeupSignalTests, SiginterruptTest, ItimerTest) + test_support.run_unittest(BasicSignalTests, InterProcessSignalTests, + WakeupSignalTests, SiginterruptTest, + ItimerTest, WindowsSignalTests) if __name__ == "__main__": From brian.curtin at gmail.com Sat Aug 7 05:57:43 2010 From: brian.curtin at gmail.com (Brian Curtin) Date: Fri, 6 Aug 2010 22:57:43 -0500 Subject: [Python-checkins] r83763 - in python/branches/py3k: Doc/library/signal.rst Lib/test/test_signal.py Misc/NEWS Modules/signalmodule.c In-Reply-To: <4C5CCC13.5070709@gmail.com> References: <20100806192732.5E323EE9D1@mail.python.org> <4C5CCC13.5070709@gmail.com> Message-ID: On Fri, Aug 6, 2010 at 21:59, Ezio Melotti wrote: > Hi, > > On 06/08/2010 22.27, brian.curtin wrote: > >> Author: brian.curtin >> Date: Fri Aug 6 21:27:32 2010 >> New Revision: 83763 >> >> Log: >> Fix #9324: Add parameter validation to signal.signal on Windows in order >> to prevent crashes. >> >> >> Modified: >> python/branches/py3k/Doc/library/signal.rst >> python/branches/py3k/Lib/test/test_signal.py >> python/branches/py3k/Misc/NEWS >> python/branches/py3k/Modules/signalmodule.c >> >> Modified: python/branches/py3k/Doc/library/signal.rst >> >> ============================================================================== >> --- python/branches/py3k/Doc/library/signal.rst (original) >> +++ python/branches/py3k/Doc/library/signal.rst Fri Aug 6 21:27:32 2010 >> @@ -230,6 +230,10 @@ >> see the :ref:`description in the type hierarchy` or see >> the >> attribute descriptions in the :mod:`inspect` module). >> >> + On Windows, :func:`signal` can only be called with :const:`SIGABRT`, >> + :const:`SIGFPE`, :const:`SIGILL`, :const:`SIGINT`, :const:`SIGSEGV`, >> or >> + :const:`SIGTERM`. A :exc:`ValueError` will be raised in any other >> case. >> + >> >> .. _signal-example: >> >> >> Modified: python/branches/py3k/Lib/test/test_signal.py >> >> ============================================================================== >> --- python/branches/py3k/Lib/test/test_signal.py (original) >> +++ python/branches/py3k/Lib/test/test_signal.py Fri Aug 6 >> 21:27:32 2010 >> @@ -9,7 +9,7 @@ >> import traceback >> import sys, os, time, errno >> >> -if sys.platform[:3] in ('win', 'os2') or sys.platform == 'riscos': >> +if sys.platform == 'os2' or sys.platform == 'riscos': >> raise unittest.SkipTest("Can't test signal on %s" % \ >> sys.platform) >> >> @@ -37,6 +37,7 @@ >> return None >> >> >> + at unittest.skipIf(sys.platform == "win32", "Not valid on Windows") >> > > In the previous chunk sys.platform[:3] was used instead of just > sys.platform. Are there some other "winXX" platform that should be skipped > too? > The sliced check was to make it more convenient to also check "os2" at the same time in the first hunk of the change. Windows is "win32" regardless of 32 or 64-bit so that check works. class InterProcessSignalTests(unittest.TestCase): >> MAX_DURATION = 20 # Entire test should last at most 20 sec. >> >> @@ -186,6 +187,7 @@ >> self.MAX_DURATION) >> >> >> + at unittest.skipIf(sys.platform == "win32", "Not valid on Windows") >> class BasicSignalTests(unittest.TestCase): >> def trivial_signal_handler(self, *args): >> pass >> @@ -208,6 +210,23 @@ >> self.assertEquals(signal.getsignal(signal.SIGHUP), hup) >> >> >> + at unittest.skipUnless(sys.platform == "win32", "Windows specific") >> +class WindowsSignalTests(unittest.TestCase): >> + def test_issue9324(self): >> + handler = lambda x, y: None >> + signal.signal(signal.SIGABRT, handler) >> + signal.signal(signal.SIGFPE, handler) >> + signal.signal(signal.SIGILL, handler) >> + signal.signal(signal.SIGINT, handler) >> + signal.signal(signal.SIGSEGV, handler) >> + signal.signal(signal.SIGTERM, handler) >> + >> + with self.assertRaises(ValueError): >> + signal.signal(-1, handler) >> + sinal.signal(7, handler) >> > > You should use two separate assertRaises here, otherwise the second line is > not executed if the first one raises an error (and if the first doesn't > raise an error but the second does the test will pass even if it shouldn't). > This also masks the typo in the second line. > Thanks for noticing this. Corrected in r83771 (py3k), r83772 (release31-maint), and r83773 (release27-maint). + >> + >> + at unittest.skipIf(sys.platform == "win32", "Not valid on Windows") >> class WakeupSignalTests(unittest.TestCase): >> TIMEOUT_FULL = 10 >> TIMEOUT_HALF = 5 >> @@ -253,14 +272,15 @@ >> os.close(self.write) >> signal.signal(signal.SIGALRM, self.alrm) >> >> + at unittest.skipIf(sys.platform == "win32", "Not valid on Windows") >> class SiginterruptTest(unittest.TestCase): >> - signum = signal.SIGUSR1 >> >> def setUp(self): >> """Install a no-op signal handler that can be set to allow >> interrupts or not, and arrange for the original signal handler to >> be >> re-installed when the test is finished. >> """ >> + self.signum = signal.SIGUSR1 >> oldhandler = signal.signal(self.signum, lambda x,y: None) >> self.addCleanup(signal.signal, self.signum, oldhandler) >> >> @@ -354,7 +374,7 @@ >> self.assertFalse(i) >> >> >> - >> + at unittest.skipIf(sys.platform == "win32", "Not valid on Windows") >> class ItimerTest(unittest.TestCase): >> def setUp(self): >> self.hndl_called = False >> @@ -463,8 +483,11 @@ >> self.assertEqual(self.hndl_called, True) >> >> def test_main(): >> - support.run_unittest(BasicSignalTests, InterProcessSignalTests, >> - WakeupSignalTests, SiginterruptTest, ItimerTest) >> + if sys.platform == "win32": >> + support.run_unittest(WindowsSignalTests) >> + else: >> + support.run_unittest(BasicSignalTests, InterProcessSignalTests, >> + WakeupSignalTests, SiginterruptTest, ItimerTest) >> > > Is this necessary? > If the tests are marked with a skip decorator they will be skipped anyway > (and also marked as skipped in the output). > Good point. Fixed in the above revisions. if __name__ == "__main__": >> >> Modified: python/branches/py3k/Misc/NEWS >> >> ============================================================================== >> --- python/branches/py3k/Misc/NEWS (original) >> +++ python/branches/py3k/Misc/NEWS Fri Aug 6 21:27:32 2010 >> @@ -24,6 +24,9 @@ >> Extensions >> ---------- >> >> +- Issue #9324: Add parameter validation to signal.signal on Windows in >> order >> + to prevent crashes. >> + >> - Issue #9526: Remove some outdated (int) casts that were preventing >> the array module from working correctly with arrays of more than >> 2**31 elements. >> >> Modified: python/branches/py3k/Modules/signalmodule.c >> >> ============================================================================== >> --- python/branches/py3k/Modules/signalmodule.c (original) >> +++ python/branches/py3k/Modules/signalmodule.c Fri Aug 6 21:27:32 2010 >> @@ -255,8 +255,23 @@ >> int sig_num; >> PyObject *old_handler; >> void (*func)(int); >> +#ifdef MS_WINDOWS >> + int cur_sig, num_valid_sigs = 6; >> + static int valid_sigs[] = {SIGABRT, SIGFPE, SIGILL, SIGINT, >> + SIGSEGV, SIGTERM}; >> + BOOL valid_sig = FALSE; >> +#endif >> if (!PyArg_ParseTuple(args, "iO:signal",&sig_num,&obj)) >> return NULL; >> +#ifdef MS_WINDOWS >> + /* Validate that sig_num is one of the allowable signals */ >> + for (cur_sig = 0; cur_sig< num_valid_sigs; cur_sig++) >> + valid_sig |= (sig_num == valid_sigs[cur_sig]); >> + if (!valid_sig) { >> + PyErr_SetString(PyExc_ValueError, "signal number out of range"); >> + return NULL; >> + } >> +#endif >> #ifdef WITH_THREAD >> if (PyThread_get_thread_ident() != main_thread) { >> PyErr_SetString(PyExc_ValueError, >> > > Best Regards, > Ezio Melotti -------------- next part -------------- An HTML attachment was scrubbed... URL: From python-checkins at python.org Sat Aug 7 06:19:49 2010 From: python-checkins at python.org (raymond.hettinger) Date: Sat, 7 Aug 2010 06:19:49 +0200 (CEST) Subject: [Python-checkins] r83774 - python/branches/py3k/Doc/whatsnew/3.2.rst Message-ID: <20100807041949.54900EE99F@mail.python.org> Author: raymond.hettinger Date: Sat Aug 7 06:19:49 2010 New Revision: 83774 Log: Fix markup Modified: python/branches/py3k/Doc/whatsnew/3.2.rst Modified: python/branches/py3k/Doc/whatsnew/3.2.rst ============================================================================== --- python/branches/py3k/Doc/whatsnew/3.2.rst (original) +++ python/branches/py3k/Doc/whatsnew/3.2.rst Sat Aug 7 06:19:49 2010 @@ -97,15 +97,13 @@ page = urllib.urlopen(query).read() return parse_lyrics(page) - To help with choosing an effective cache size, the wrapped function - is instrumented with two attributes 'hits' and 'misses':: + To help with choosing an effective cache size, the wrapped function + is instrumented with two attributes *hits* and *misses*:: >>> for song in user_requests: ... find_lyrics(song) - >>> print find_lyrics.hits - 4805 - >>> print find_lyrics.misses - 980 + >>> print(find_lyrics.hits, find_lyrics.misses) + 4805 980 (Contributed by Raymond Hettinger) From python-checkins at python.org Sat Aug 7 07:36:54 2010 From: python-checkins at python.org (raymond.hettinger) Date: Sat, 7 Aug 2010 07:36:54 +0200 (CEST) Subject: [Python-checkins] r83775 - python/branches/py3k/Doc/library/itertools.rst Message-ID: <20100807053654.17753EEA43@mail.python.org> Author: raymond.hettinger Date: Sat Aug 7 07:36:53 2010 New Revision: 83775 Log: Add partition recipe to itertools docs. Modified: python/branches/py3k/Doc/library/itertools.rst Modified: python/branches/py3k/Doc/library/itertools.rst ============================================================================== --- python/branches/py3k/Doc/library/itertools.rst (original) +++ python/branches/py3k/Doc/library/itertools.rst Sat Aug 7 07:36:53 2010 @@ -653,6 +653,12 @@ pending -= 1 nexts = cycle(islice(nexts, pending)) + def partition(pred, iterable): + 'Use a predicate to partition entries into false entries and true entries' + # partition(is_odd, range(10)) --> 0 2 4 6 8 and 1 3 5 7 9 + t1, t2 = tee(iterable) + return filterfalse(pred, t1), filter(pred, t2) + def powerset(iterable): "powerset([1,2,3]) --> () (1,) (2,) (3,) (1,2) (1,3) (2,3) (1,2,3)" s = list(iterable) From python-checkins at python.org Sat Aug 7 07:54:08 2010 From: python-checkins at python.org (raymond.hettinger) Date: Sat, 7 Aug 2010 07:54:08 +0200 (CEST) Subject: [Python-checkins] r83776 - python/branches/py3k/Objects/iterobject.c Message-ID: <20100807055408.EF2F5EE992@mail.python.org> Author: raymond.hettinger Date: Sat Aug 7 07:54:08 2010 New Revision: 83776 Log: Fix nit (sentinel on lhs of comparison). Modified: python/branches/py3k/Objects/iterobject.c Modified: python/branches/py3k/Objects/iterobject.c ============================================================================== --- python/branches/py3k/Objects/iterobject.c (original) +++ python/branches/py3k/Objects/iterobject.c Sat Aug 7 07:54:08 2010 @@ -177,9 +177,7 @@ Py_DECREF(args); if (result != NULL) { int ok; - ok = PyObject_RichCompareBool(result, - it->it_sentinel, - Py_EQ); + ok = PyObject_RichCompareBool(it->it_sentinel, result, Py_EQ); if (ok == 0) return result; /* Common case, fast path */ Py_DECREF(result); From python-checkins at python.org Sat Aug 7 09:36:56 2010 From: python-checkins at python.org (raymond.hettinger) Date: Sat, 7 Aug 2010 09:36:56 +0200 (CEST) Subject: [Python-checkins] r83777 - python/branches/py3k/Doc/library/bisect.rst Message-ID: <20100807073656.164D2EE992@mail.python.org> Author: raymond.hettinger Date: Sat Aug 7 09:36:55 2010 New Revision: 83777 Log: Improve the docs for bisect to cover common searching tasks. Modified: python/branches/py3k/Doc/library/bisect.rst Modified: python/branches/py3k/Doc/library/bisect.rst ============================================================================== --- python/branches/py3k/Doc/library/bisect.rst (original) +++ python/branches/py3k/Doc/library/bisect.rst Sat Aug 7 09:36:55 2010 @@ -39,6 +39,9 @@ ``a.insert(bisect.bisect_left(a, x, lo, hi), x)``. This assumes that *a* is already sorted. + Also note that while the fast search step is O(log n), the slower insertion + step is O(n), so the overall operation is slow. + .. function:: insort_right(a, x, lo=0, hi=len(a)) insort(a, x, lo=0, hi=len(a)) @@ -46,9 +49,53 @@ Similar to :func:`insort_left`, but inserting *x* in *a* after any existing entries of *x*. + Also note that while the fast search step is O(log n), the slower insertion + step is O(n), so the overall operation is slow. + +Searching Sorted Lists +---------------------- + +The above :func:`bisect` functions are useful for finding insertion points, but +can be tricky or awkward to use for common searching tasks. The following three +functions show how to transform them into the standard lookups for sorted +lists:: + + def find(a, key): + '''Find item with a key-value equal to key. + Raise ValueError if no such item exists. + + ''' + i = bisect_left(a, key) + if i < len(a) and a[i] == key: + return a[i] + raise ValueError('No item found with key equal to: %r' % (key,)) + + def find_le(a, key): + '''Find largest item with a key-value less-than or equal to key. + Raise ValueError if no such item exists. + If multiple key-values are equal, return the leftmost. + + ''' + i = bisect_left(a, key) + if i < len(a) and a[i] == key: + return a[i] + if i == 0: + raise ValueError('No item found with key at or below: %r' % (key,)) + return a[i-1] + + def find_ge(a, key): + '''Find smallest item with a key-value greater-than or equal to key. + Raise ValueError if no such item exists. + If multiple key-values are equal, return the leftmost. + + ''' + i = bisect_left(a, key) + if i == len(a): + raise ValueError('No item found with key at or above: %r' % (key,)) + return a[i] -Examples --------- +Other Examples +-------------- .. _bisect-example: @@ -87,3 +134,10 @@ ('red', 5) >>> data[bisect_left(keys, 8)] ('yellow', 8) + +.. seealso:: + + `SortedCollection recipe + `_ that + encapsulates precomputed keys, allowing straight-forward insertion and + searching using a *key* function. From python-checkins at python.org Sat Aug 7 12:09:35 2010 From: python-checkins at python.org (victor.stinner) Date: Sat, 7 Aug 2010 12:09:35 +0200 (CEST) Subject: [Python-checkins] r83778 - in python/branches/py3k/Lib/test: test_import.py test_sax.py test_sys.py test_urllib.py test_urllib2.py test_xml_etree.py Message-ID: <20100807100935.4C911EEA3B@mail.python.org> Author: victor.stinner Date: Sat Aug 7 12:09:35 2010 New Revision: 83778 Log: Issue #9425: skip tests if a filename is not encodable Modified: python/branches/py3k/Lib/test/test_import.py python/branches/py3k/Lib/test/test_sax.py python/branches/py3k/Lib/test/test_sys.py python/branches/py3k/Lib/test/test_urllib.py python/branches/py3k/Lib/test/test_urllib2.py python/branches/py3k/Lib/test/test_xml_etree.py Modified: python/branches/py3k/Lib/test/test_import.py ============================================================================== --- python/branches/py3k/Lib/test/test_import.py (original) +++ python/branches/py3k/Lib/test/test_import.py Sat Aug 7 12:09:35 2010 @@ -291,6 +291,11 @@ def test_import_by_filename(self): path = os.path.abspath(TESTFN) + encoding = sys.getfilesystemencoding() + try: + path.encode(encoding) + except UnicodeEncodeError: + self.skipTest('path is not encodable to {}'.format(encoding)) with self.assertRaises(ImportError) as c: __import__(path) self.assertEqual("Import by filename is not supported.", Modified: python/branches/py3k/Lib/test/test_sax.py ============================================================================== --- python/branches/py3k/Lib/test/test_sax.py (original) +++ python/branches/py3k/Lib/test/test_sax.py Sat Aug 7 12:09:35 2010 @@ -18,6 +18,11 @@ TEST_XMLFILE = findfile("test.xml", subdir="xmltestdata") TEST_XMLFILE_OUT = findfile("test.xml.out", subdir="xmltestdata") +try: + TEST_XMLFILE.encode("utf8") + TEST_XMLFILE_OUT.encode("utf8") +except UnicodeEncodeError: + raise unittest.SkipTest("filename is not encodable to utf8") ns_uri = "http://www.python.org/xml-ns/saxtest/" Modified: python/branches/py3k/Lib/test/test_sys.py ============================================================================== --- python/branches/py3k/Lib/test/test_sys.py (original) +++ python/branches/py3k/Lib/test/test_sys.py Sat Aug 7 12:09:35 2010 @@ -509,8 +509,10 @@ p = subprocess.Popen([sys.executable, "-c", code], stderr=subprocess.PIPE) stdout, stderr = p.communicate() self.assertEqual(p.returncode, 1) - self.assert_(b"UnicodeEncodeError:" in stderr, - "%r not in %s" % (b"UniodeEncodeError:", ascii(stderr))) + self.assertIn( + br"UnicodeEncodeError: 'utf-8' codec can't encode character " + br"'\udcff' in position 7: surrogates not allowed", + stderr) def test_sys_flags(self): self.assertTrue(sys.flags) Modified: python/branches/py3k/Lib/test/test_urllib.py ============================================================================== --- python/branches/py3k/Lib/test/test_urllib.py (original) +++ python/branches/py3k/Lib/test/test_urllib.py Sat Aug 7 12:09:35 2010 @@ -232,8 +232,12 @@ except: pass def constructLocalFileUrl(self, filePath): - return "file://%s" % urllib.request.pathname2url( - os.path.abspath(filePath)) + filePath = os.path.abspath(filePath) + try: + filePath.encode("utf8") + except UnicodeEncodeError: + raise unittest.SkipTest("filePath is not encodable to utf8") + return "file://%s" % urllib.request.pathname2url(filePath) def createNewTempFile(self, data=b""): """Creates a new temporary file containing the specified data, Modified: python/branches/py3k/Lib/test/test_urllib2.py ============================================================================== --- python/branches/py3k/Lib/test/test_urllib2.py (original) +++ python/branches/py3k/Lib/test/test_urllib2.py Sat Aug 7 12:09:35 2010 @@ -597,6 +597,10 @@ def sanepathname2url(path): + try: + path.encode("utf8") + except UnicodeEncodeError: + raise unittest.SkipTest("path is not encodable to utf8") urlpath = urllib.request.pathname2url(path) if os.name == "nt" and urlpath.startswith("///"): urlpath = urlpath[2:] Modified: python/branches/py3k/Lib/test/test_xml_etree.py ============================================================================== --- python/branches/py3k/Lib/test/test_xml_etree.py (original) +++ python/branches/py3k/Lib/test/test_xml_etree.py Sat Aug 7 12:09:35 2010 @@ -13,6 +13,7 @@ import sys import cgi +import unittest from test import support from test.support import findfile @@ -20,6 +21,10 @@ from xml.etree import ElementTree as ET SIMPLE_XMLFILE = findfile("simple.xml", subdir="xmltestdata") +try: + SIMPLE_XMLFILE.encode("utf8") +except UnicodeEncodeError: + raise unittest.SkipTest("filename is not encodable to utf8") SIMPLE_NS_XMLFILE = findfile("simple-ns.xml", subdir="xmltestdata") SAMPLE_XML = """\ From python-checkins at python.org Sat Aug 7 12:57:17 2010 From: python-checkins at python.org (victor.stinner) Date: Sat, 7 Aug 2010 12:57:17 +0200 (CEST) Subject: [Python-checkins] r83779 - python/branches/py3k/Modules/main.c Message-ID: <20100807105717.2F31FEE987@mail.python.org> Author: victor.stinner Date: Sat Aug 7 12:57:17 2010 New Revision: 83779 Log: Issue #9425: Create run_command() subfunction Use PyUnicode_AsUTF8String() instead of _PyUnicode_AsString() Modified: python/branches/py3k/Modules/main.c Modified: python/branches/py3k/Modules/main.c ============================================================================== --- python/branches/py3k/Modules/main.c (original) +++ python/branches/py3k/Modules/main.c Sat Aug 7 12:57:17 2010 @@ -253,6 +253,28 @@ } } +static int +run_command(wchar_t *command, PyCompilerFlags *cf) +{ + PyObject *unicode, *bytes; + int ret; + + unicode = PyUnicode_FromWideChar(command, -1); + if (unicode == NULL) + goto error; + bytes = PyUnicode_AsUTF8String(unicode); + Py_DECREF(unicode); + if (bytes == NULL) + goto error; + ret = PyRun_SimpleStringFlags(PyBytes_AsString(bytes), cf); + Py_DECREF(bytes); + return ret != 0; + +error: + PyErr_Print(); + return 1; +} + /* Main program */ @@ -564,22 +586,8 @@ } if (command) { - char *commandStr; - PyObject *commandObj = PyUnicode_FromWideChar( - command, wcslen(command)); + sts = run_command(command, &cf); free(command); - if (commandObj != NULL) - commandStr = _PyUnicode_AsString(commandObj); - else - commandStr = NULL; - if (commandStr != NULL) { - sts = PyRun_SimpleStringFlags(commandStr, &cf) != 0; - Py_DECREF(commandObj); - } - else { - PyErr_Print(); - sts = 1; - } } else if (module) { sts = RunModule(module, 1); } From python-checkins at python.org Sat Aug 7 14:33:36 2010 From: python-checkins at python.org (mark.dickinson) Date: Sat, 7 Aug 2010 14:33:36 +0200 (CEST) Subject: [Python-checkins] r83780 - in python/branches/py3k: Lib/test/test_curses.py Misc/NEWS Message-ID: <20100807123336.B1D10EEA0C@mail.python.org> Author: mark.dickinson Date: Sat Aug 7 14:33:36 2010 New Revision: 83780 Log: Issue #8433: Fix test_curses failure for platforms with recent versions of ncurses. Modified: python/branches/py3k/Lib/test/test_curses.py python/branches/py3k/Misc/NEWS Modified: python/branches/py3k/Lib/test/test_curses.py ============================================================================== --- python/branches/py3k/Lib/test/test_curses.py (original) +++ python/branches/py3k/Lib/test/test_curses.py Sat Aug 7 14:33:36 2010 @@ -220,8 +220,8 @@ if availmask != 0: curses.mouseinterval(10) # just verify these don't cause errors + curses.ungetmouse(0, 0, 0, 0, curses.BUTTON1_PRESSED) m = curses.getmouse() - curses.ungetmouse(*m) if hasattr(curses, 'is_term_resized'): curses.is_term_resized(*stdscr.getmaxyx()) Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Sat Aug 7 14:33:36 2010 @@ -126,6 +126,8 @@ Tests ----- +- Issue #8433: Fix test_curses failure with newer versions of ncurses. + - Issue #9496: Provide a test suite for the rlcompleter module. Patch by Michele Orr?. From ezio.melotti at gmail.com Sat Aug 7 14:49:05 2010 From: ezio.melotti at gmail.com (Ezio Melotti) Date: Sat, 07 Aug 2010 15:49:05 +0300 Subject: [Python-checkins] r83778 - in python/branches/py3k/Lib/test: test_import.py test_sax.py test_sys.py test_urllib.py test_urllib2.py test_xml_etree.py In-Reply-To: <20100807100935.4C911EEA3B@mail.python.org> References: <20100807100935.4C911EEA3B@mail.python.org> Message-ID: <4C5D5641.4040509@gmail.com> Hi, On 07/08/2010 13.09, victor.stinner wrote: > Author: victor.stinner > Date: Sat Aug 7 12:09:35 2010 > New Revision: 83778 > > Log: > Issue #9425: skip tests if a filename is not encodable > > Modified: > python/branches/py3k/Lib/test/test_import.py > python/branches/py3k/Lib/test/test_sax.py > python/branches/py3k/Lib/test/test_sys.py > python/branches/py3k/Lib/test/test_urllib.py > python/branches/py3k/Lib/test/test_urllib2.py > python/branches/py3k/Lib/test/test_xml_etree.py > > Modified: python/branches/py3k/Lib/test/test_import.py > ============================================================================== > --- python/branches/py3k/Lib/test/test_import.py (original) > +++ python/branches/py3k/Lib/test/test_import.py Sat Aug 7 12:09:35 2010 > @@ -291,6 +291,11 @@ > > def test_import_by_filename(self): > path = os.path.abspath(TESTFN) > + encoding = sys.getfilesystemencoding() > + try: > + path.encode(encoding) > + except UnicodeEncodeError: > + self.skipTest('path is not encodable to {}'.format(encoding)) > with self.assertRaises(ImportError) as c: > __import__(path) > self.assertEqual("Import by filename is not supported.", > > Modified: python/branches/py3k/Lib/test/test_sax.py > ============================================================================== > --- python/branches/py3k/Lib/test/test_sax.py (original) > +++ python/branches/py3k/Lib/test/test_sax.py Sat Aug 7 12:09:35 2010 > @@ -18,6 +18,11 @@ > > TEST_XMLFILE = findfile("test.xml", subdir="xmltestdata") > TEST_XMLFILE_OUT = findfile("test.xml.out", subdir="xmltestdata") > +try: > + TEST_XMLFILE.encode("utf8") > + TEST_XMLFILE_OUT.encode("utf8") > +except UnicodeEncodeError: > + raise unittest.SkipTest("filename is not encodable to utf8") > > ns_uri = "http://www.python.org/xml-ns/saxtest/" > > > Modified: python/branches/py3k/Lib/test/test_sys.py > ============================================================================== > --- python/branches/py3k/Lib/test/test_sys.py (original) > +++ python/branches/py3k/Lib/test/test_sys.py Sat Aug 7 12:09:35 2010 > @@ -509,8 +509,10 @@ > p = subprocess.Popen([sys.executable, "-c", code], stderr=subprocess.PIPE) > stdout, stderr = p.communicate() > self.assertEqual(p.returncode, 1) > - self.assert_(b"UnicodeEncodeError:" in stderr, > - "%r not in %s" % (b"UniodeEncodeError:", ascii(stderr))) > + self.assertIn( > + br"UnicodeEncodeError: 'utf-8' codec can't encode character " > + br"'\udcff' in position 7: surrogates not allowed", > + stderr) This caused some failures in the buildbots: test test_sys failed -- Traceback (most recent call last): File "/home2/buildbot/slave/3.x.loewis-sun/build/Lib/test/test_sys.py", line 515, in test_main_invalid_unicode stderr) AssertionError: b"UnicodeEncodeError: 'utf-8' codec can't encode character '\\udcff' in position 7: surrogates not allowed" not found in b'Traceback (most recent call last):\n File "", line 1, in\nUnicodeEncodeError: \'ascii\' codec can\'t encode character \'\\xff\' in position 0: ordinal not in range(128)\n[32513 refs]\n' See http://www.python.org/dev/buildbot/all/builders/sparc%20solaris10%20gcc%203.x/builds/1338/steps/test/logs/stdio > > def test_sys_flags(self): > self.assertTrue(sys.flags) > > Modified: python/branches/py3k/Lib/test/test_urllib.py > ============================================================================== > --- python/branches/py3k/Lib/test/test_urllib.py (original) > +++ python/branches/py3k/Lib/test/test_urllib.py Sat Aug 7 12:09:35 2010 > @@ -232,8 +232,12 @@ > except: pass > > def constructLocalFileUrl(self, filePath): > - return "file://%s" % urllib.request.pathname2url( > - os.path.abspath(filePath)) > + filePath = os.path.abspath(filePath) > + try: > + filePath.encode("utf8") > + except UnicodeEncodeError: > + raise unittest.SkipTest("filePath is not encodable to utf8") > + return "file://%s" % urllib.request.pathname2url(filePath) > > def createNewTempFile(self, data=b""): > """Creates a new temporary file containing the specified data, > > Modified: python/branches/py3k/Lib/test/test_urllib2.py > ============================================================================== > --- python/branches/py3k/Lib/test/test_urllib2.py (original) > +++ python/branches/py3k/Lib/test/test_urllib2.py Sat Aug 7 12:09:35 2010 > @@ -597,6 +597,10 @@ > > > def sanepathname2url(path): > + try: > + path.encode("utf8") > + except UnicodeEncodeError: > + raise unittest.SkipTest("path is not encodable to utf8") > urlpath = urllib.request.pathname2url(path) > if os.name == "nt" and urlpath.startswith("///"): > urlpath = urlpath[2:] > > Modified: python/branches/py3k/Lib/test/test_xml_etree.py > ============================================================================== > --- python/branches/py3k/Lib/test/test_xml_etree.py (original) > +++ python/branches/py3k/Lib/test/test_xml_etree.py Sat Aug 7 12:09:35 2010 > @@ -13,6 +13,7 @@ > > import sys > import cgi > +import unittest > > from test import support > from test.support import findfile > @@ -20,6 +21,10 @@ > from xml.etree import ElementTree as ET > > SIMPLE_XMLFILE = findfile("simple.xml", subdir="xmltestdata") > +try: > + SIMPLE_XMLFILE.encode("utf8") > +except UnicodeEncodeError: > + raise unittest.SkipTest("filename is not encodable to utf8") > SIMPLE_NS_XMLFILE = findfile("simple-ns.xml", subdir="xmltestdata") > > SAMPLE_XML = """\ > _______________________________________________ > Python-checkins mailing list > Python-checkins at python.org > http://mail.python.org/mailman/listinfo/python-checkins > Best Regards, Ezio Melotti From python-checkins at python.org Sat Aug 7 15:05:49 2010 From: python-checkins at python.org (mark.dickinson) Date: Sat, 7 Aug 2010 15:05:49 +0200 (CEST) Subject: [Python-checkins] r83781 - in python/branches/release31-maint: Lib/test/test_curses.py Misc/NEWS Message-ID: <20100807130549.64EFEEE984@mail.python.org> Author: mark.dickinson Date: Sat Aug 7 15:05:49 2010 New Revision: 83781 Log: Merged revisions 83780 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83780 | mark.dickinson | 2010-08-07 13:33:36 +0100 (Sat, 07 Aug 2010) | 1 line Issue #8433: Fix test_curses failure for platforms with recent versions of ncurses. ........ Modified: python/branches/release31-maint/ (props changed) python/branches/release31-maint/Lib/test/test_curses.py python/branches/release31-maint/Misc/NEWS Modified: python/branches/release31-maint/Lib/test/test_curses.py ============================================================================== --- python/branches/release31-maint/Lib/test/test_curses.py (original) +++ python/branches/release31-maint/Lib/test/test_curses.py Sat Aug 7 15:05:49 2010 @@ -220,8 +220,8 @@ if availmask != 0: curses.mouseinterval(10) # just verify these don't cause errors + curses.ungetmouse(0, 0, 0, 0, curses.BUTTON1_PRESSED) m = curses.getmouse() - curses.ungetmouse(*m) if hasattr(curses, 'is_term_resized'): curses.is_term_resized(*stdscr.getmaxyx()) Modified: python/branches/release31-maint/Misc/NEWS ============================================================================== --- python/branches/release31-maint/Misc/NEWS (original) +++ python/branches/release31-maint/Misc/NEWS Sat Aug 7 15:05:49 2010 @@ -446,6 +446,8 @@ Tests ----- +- Issue #8433: Fix test_curses failure with newer versions of ncurses. + - Issue #9496: Provide a test suite for the rlcompleter module. Patch by Michele Orr?. From python-checkins at python.org Sat Aug 7 15:07:11 2010 From: python-checkins at python.org (mark.dickinson) Date: Sat, 7 Aug 2010 15:07:11 +0200 (CEST) Subject: [Python-checkins] r83782 - in python/branches/release27-maint: Lib/test/test_curses.py Misc/NEWS Message-ID: <20100807130711.F1930EE98F@mail.python.org> Author: mark.dickinson Date: Sat Aug 7 15:07:11 2010 New Revision: 83782 Log: Merged revisions 83780 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r83780 | mark.dickinson | 2010-08-07 13:33:36 +0100 (Sat, 07 Aug 2010) | 1 line Issue #8433: Fix test_curses failure for platforms with recent versions of ncurses. ........ Modified: python/branches/release27-maint/ (props changed) python/branches/release27-maint/Lib/test/test_curses.py python/branches/release27-maint/Misc/NEWS Modified: python/branches/release27-maint/Lib/test/test_curses.py ============================================================================== --- python/branches/release27-maint/Lib/test/test_curses.py (original) +++ python/branches/release27-maint/Lib/test/test_curses.py Sat Aug 7 15:07:11 2010 @@ -218,8 +218,8 @@ if availmask != 0: curses.mouseinterval(10) # just verify these don't cause errors + curses.ungetmouse(0, 0, 0, 0, curses.BUTTON1_PRESSED) m = curses.getmouse() - curses.ungetmouse(*m) if hasattr(curses, 'is_term_resized'): curses.is_term_resized(*stdscr.getmaxyx()) Modified: python/branches/release27-maint/Misc/NEWS ============================================================================== --- python/branches/release27-maint/Misc/NEWS (original) +++ python/branches/release27-maint/Misc/NEWS Sat Aug 7 15:07:11 2010 @@ -194,6 +194,8 @@ Tests ----- +- Issue #8433: Fix test_curses failure with newer versions of ncurses. + - Issue #9496: Provide a test suite for the rlcompleter module. Patch by Michele Orr?. From python-checkins at python.org Sat Aug 7 18:34:26 2010 From: python-checkins at python.org (victor.stinner) Date: Sat, 7 Aug 2010 18:34:26 +0200 (CEST) Subject: [Python-checkins] r83783 - python/branches/py3k/Modules/main.c Message-ID: <20100807163426.23CFCEEA5F@mail.python.org> Author: victor.stinner Date: Sat Aug 7 18:34:25 2010 New Revision: 83783 Log: Issue #9425: Create run_file() subfunction * Call Py_MakePendingCalls() before converting the filename from wchar_t* to char* * Use PyUnicode_AsUTF8String() instead of _PyUnicode_AsString() Modified: python/branches/py3k/Modules/main.c Modified: python/branches/py3k/Modules/main.c ============================================================================== --- python/branches/py3k/Modules/main.c (original) +++ python/branches/py3k/Modules/main.c Sat Aug 7 18:34:25 2010 @@ -275,6 +275,40 @@ return 1; } +static int +run_file(FILE *fp, const wchar_t *filename, PyCompilerFlags *p_cf) +{ + PyObject *unicode, *bytes = NULL; + char *filename_str; + int run; + + /* call pending calls like signal handlers (SIGINT) */ + if (Py_MakePendingCalls() == -1) { + PyErr_Print(); + return 1; + } + + if (filename) { + unicode = PyUnicode_FromWideChar(filename, wcslen(filename)); + if (unicode != NULL) { + bytes = PyUnicode_AsUTF8String(unicode); + Py_DECREF(unicode); + } + if (bytes != NULL) + filename_str = PyBytes_AsString(bytes); + else { + PyErr_Clear(); + filename_str = ""; + } + } + else + filename_str = ""; + + run = PyRun_AnyFileExFlags(fp, filename_str, filename != NULL, p_cf); + Py_XDECREF(bytes); + return run != 0; +} + /* Main program */ @@ -644,30 +678,8 @@ } } - if (sts==-1) { - PyObject *filenameObj = NULL; - char *p_cfilename = ""; - if (filename) { - filenameObj = PyUnicode_FromWideChar( - filename, wcslen(filename)); - if (filenameObj != NULL) - p_cfilename = _PyUnicode_AsString(filenameObj); - else - p_cfilename = ""; - } - /* call pending calls like signal handlers (SIGINT) */ - if (Py_MakePendingCalls() == -1) { - PyErr_Print(); - sts = 1; - } else { - sts = PyRun_AnyFileExFlags( - fp, - p_cfilename, - filename != NULL, &cf) != 0; - } - Py_XDECREF(filenameObj); - } - + if (sts == -1) + sts = run_file(fp, filename, &cf); } /* Check this environment variable at the end, to give programs the From python-checkins at python.org Sat Aug 7 23:31:55 2010 From: python-checkins at python.org (raymond.hettinger) Date: Sat, 7 Aug 2010 23:31:55 +0200 (CEST) Subject: [Python-checkins] r83784 - python/branches/py3k/Doc/library/bisect.rst Message-ID: <20100807213155.53BF8EEA45@mail.python.org> Author: raymond.hettinger Date: Sat Aug 7 23:31:55 2010 New Revision: 83784 Log: Clean-up docstring in examples. Modified: python/branches/py3k/Doc/library/bisect.rst Modified: python/branches/py3k/Doc/library/bisect.rst ============================================================================== --- python/branches/py3k/Doc/library/bisect.rst (original) +++ python/branches/py3k/Doc/library/bisect.rst Sat Aug 7 23:31:55 2010 @@ -61,7 +61,7 @@ lists:: def find(a, key): - '''Find item with a key-value equal to key. + '''Find leftmost item exact equal to the key. Raise ValueError if no such item exists. ''' @@ -71,9 +71,9 @@ raise ValueError('No item found with key equal to: %r' % (key,)) def find_le(a, key): - '''Find largest item with a key-value less-than or equal to key. + '''Find largest item less-than or equal to key. Raise ValueError if no such item exists. - If multiple key-values are equal, return the leftmost. + If multiple keys are equal, return the leftmost. ''' i = bisect_left(a, key) @@ -84,9 +84,9 @@ return a[i-1] def find_ge(a, key): - '''Find smallest item with a key-value greater-than or equal to key. + '''Find smallest item greater-than or equal to key. Raise ValueError if no such item exists. - If multiple key-values are equal, return the leftmost. + If multiple keys are equal, return the leftmost. ''' i = bisect_left(a, key) From python-checkins at python.org Sat Aug 7 23:32:13 2010 From: python-checkins at python.org (benjamin.peterson) Date: Sat, 7 Aug 2010 23:32:13 +0200 (CEST) Subject: [Python-checkins] r83785 - python/branches/py3k/Lib/smtpd.py Message-ID: <20100807213213.06528EEA43@mail.python.org> Author: benjamin.peterson Date: Sat Aug 7 23:32:12 2010 New Revision: 83785 Log: kill outdated comment Modified: python/branches/py3k/Lib/smtpd.py Modified: python/branches/py3k/Lib/smtpd.py ============================================================================== --- python/branches/py3k/Lib/smtpd.py (original) +++ python/branches/py3k/Lib/smtpd.py Sat Aug 7 23:32:12 2010 @@ -59,7 +59,6 @@ # gets forwarded to a real backend smtpd, as with PureProxy. Again, errors # are not handled correctly yet. # -# Please note that this script requires Python 2.0 # # Author: Barry Warsaw # From python-checkins at python.org Sat Aug 7 23:40:15 2010 From: python-checkins at python.org (raymond.hettinger) Date: Sat, 7 Aug 2010 23:40:15 +0200 (CEST) Subject: [Python-checkins] r83786 - python/branches/release31-maint/Doc/library/bisect.rst Message-ID: <20100807214015.01317EE981@mail.python.org> Author: raymond.hettinger Date: Sat Aug 7 23:40:14 2010 New Revision: 83786 Log: Backport doc updates for the bisect module Modified: python/branches/release31-maint/Doc/library/bisect.rst Modified: python/branches/release31-maint/Doc/library/bisect.rst ============================================================================== --- python/branches/release31-maint/Doc/library/bisect.rst (original) +++ python/branches/release31-maint/Doc/library/bisect.rst Sat Aug 7 23:40:14 2010 @@ -39,6 +39,9 @@ ``a.insert(bisect.bisect_left(a, x, lo, hi), x)``. This assumes that *a* is already sorted. + Also note that while the fast search step is O(log n), the slower insertion + step is O(n), so the overall operation is slow. + .. function:: insort_right(a, x, lo=0, hi=len(a)) insort(a, x, lo=0, hi=len(a)) @@ -46,9 +49,53 @@ Similar to :func:`insort_left`, but inserting *x* in *a* after any existing entries of *x*. + Also note that while the fast search step is O(log n), the slower insertion + step is O(n), so the overall operation is slow. + +Searching Sorted Lists +---------------------- + +The above :func:`bisect` functions are useful for finding insertion points, but +can be tricky or awkward to use for common searching tasks. The following three +functions show how to transform them into the standard lookups for sorted +lists:: + + def find(a, key): + '''Find leftmost item exact equal to the key. + Raise ValueError if no such item exists. + + ''' + i = bisect_left(a, key) + if i < len(a) and a[i] == key: + return a[i] + raise ValueError('No item found with key equal to: %r' % (key,)) + + def find_le(a, key): + '''Find largest item less-than or equal to key. + Raise ValueError if no such item exists. + If multiple keys are equal, return the leftmost. + + ''' + i = bisect_left(a, key) + if i < len(a) and a[i] == key: + return a[i] + if i == 0: + raise ValueError('No item found with key at or below: %r' % (key,)) + return a[i-1] + + def find_ge(a, key): + '''Find smallest item greater-than or equal to key. + Raise ValueError if no such item exists. + If multiple keys are equal, return the leftmost. + + ''' + i = bisect_left(a, key) + if i == len(a): + raise ValueError('No item found with key at or above: %r' % (key,)) + return a[i] -Examples --------- +Other Examples +-------------- .. _bisect-example: @@ -87,3 +134,10 @@ ('red', 5) >>> data[bisect_left(keys, 8)] ('yellow', 8) + +.. seealso:: + + `SortedCollection recipe + `_ that + encapsulates precomputed keys, allowing straight-forward insertion and + searching using a *key* function. From python-checkins at python.org Sun Aug 8 00:36:32 2010 From: python-checkins at python.org (benjamin.peterson) Date: Sun, 8 Aug 2010 00:36:32 +0200 (CEST) Subject: [Python-checkins] r83788 - sandbox/trunk/2to3/README Message-ID: <20100807223632.C582AEEA65@mail.python.org> Author: benjamin.peterson Date: Sun Aug 8 00:36:32 2010 New Revision: 83788 Log: 2.5 is okay, too, now Modified: sandbox/trunk/2to3/README Modified: sandbox/trunk/2to3/README ============================================================================== --- sandbox/trunk/2to3/README (original) +++ sandbox/trunk/2to3/README Sun Aug 8 00:36:32 2010 @@ -13,7 +13,7 @@ Run ``./2to3`` to convert stdin (``-``), files or directories given as arguments. -2to3 must be run with at least Python 2.6. The intended path for migrating to +2to3 must be run with at least Python 2.5. The intended path for migrating to Python 3.x is to first migrate to 2.6 (in order to take advantage of Python 2.6's runtime compatibility checks). From python-checkins at python.org Sun Aug 8 00:45:14 2010 From: python-checkins at python.org (benjamin.peterson) Date: Sun, 8 Aug 2010 00:45:14 +0200 (CEST) Subject: [Python-checkins] r83789 - sandbox/trunk/2to3/lib2to3/fixes/fix_operator.py Message-ID: <20100807224514.69E5AEEA2C@mail.python.org> Author: benjamin.peterson Date: Sun Aug 8 00:45:14 2010 New Revision: 83789 Log: cleanup and use unicode consistently Modified: sandbox/trunk/2to3/lib2to3/fixes/fix_operator.py Modified: sandbox/trunk/2to3/lib2to3/fixes/fix_operator.py ============================================================================== --- sandbox/trunk/2to3/lib2to3/fixes/fix_operator.py (original) +++ sandbox/trunk/2to3/lib2to3/fixes/fix_operator.py Sun Aug 8 00:45:14 2010 @@ -10,8 +10,9 @@ """ # Local imports -from .. import fixer_base -from ..fixer_util import Call, Name, String, touch_import +from lib2to3 import fixer_base +from lib2to3.fixer_util import Call, Name, String, touch_import + class FixOperator(fixer_base.BaseFix): @@ -29,8 +30,9 @@ """ % dict(methods=methods, obj=obj) def transform(self, node, results): - if self._check_method(node, results): - return self._get_method(results)(node, results) + method = self._check_method(node, results) + if method is not None: + return method(node, results) def _sequenceIncludes(self, node, results): """operator.contains(%s)""" @@ -52,15 +54,15 @@ def _isSequenceType(self, node, results): """isinstance(%s, collections.Sequence)""" - return self._handle_type2abc(node, results, "collections", "Sequence") + return self._handle_type2abc(node, results, u"collections", u"Sequence") def _isMappingType(self, node, results): """isinstance(%s, collections.Mapping)""" - return self._handle_type2abc(node, results, "collections", "Mapping") + return self._handle_type2abc(node, results, u"collections", u"Mapping") def _isNumberType(self, node, results): """isinstance(%s, numbers.Number)""" - return self._handle_type2abc(node, results, "numbers", "Number") + return self._handle_type2abc(node, results, u"numbers", u"Number") def _handle_rename(self, node, results, name): method = results["method"][0] @@ -69,19 +71,17 @@ def _handle_type2abc(self, node, results, module, abc): touch_import(None, module, node) - obj = results['obj'] - args = [obj.clone(), String(', ' + ".".join([module, abc]))] - return Call(Name('isinstance'), args, prefix=node.get_prefix()) - - def _get_method(self, results): - return getattr(self, "_" + results["method"][0].value) + obj = results["obj"] + args = [obj.clone(), String(u", " + u".".join([module, abc]))] + return Call(Name(u"isinstance"), args, prefix=node.prefix) def _check_method(self, node, results): - method = self._get_method(results) + method = getattr(self, "_" + results["method"][0].value.encode("ascii")) if callable(method): if "module" in results: - return True + return method else: - invocation_str = method.__doc__ % str(results["obj"]) - self.warning(node, "You should use '%s' here." % invocation_str) - return False + sub = (unicode(results["obj"]),) + invocation_str = unicode(method.__doc__) % sub + self.warning(node, u"You should use '%s' here." % invocation_str) + return None From python-checkins at python.org Sun Aug 8 00:52:06 2010 From: python-checkins at python.org (benjamin.peterson) Date: Sun, 8 Aug 2010 00:52:06 +0200 (CEST) Subject: [Python-checkins] r83790 - sandbox/trunk/2to3/lib2to3/fixes/fix_urllib.py Message-ID: <20100807225206.DC342EE981@mail.python.org> Author: benjamin.peterson Date: Sun Aug 8 00:52:06 2010 New Revision: 83790 Log: unicode literal Modified: sandbox/trunk/2to3/lib2to3/fixes/fix_urllib.py Modified: sandbox/trunk/2to3/lib2to3/fixes/fix_urllib.py ============================================================================== --- sandbox/trunk/2to3/lib2to3/fixes/fix_urllib.py (original) +++ sandbox/trunk/2to3/lib2to3/fixes/fix_urllib.py Sun Aug 8 00:52:06 2010 @@ -123,7 +123,7 @@ for member in members: member = member.value # we only care about the actual members - if member != ',': + if member != u',': for change in MAPPING[mod_member.value]: if member in change[1]: if change[0] in mod_dict: From python-checkins at python.org Sun Aug 8 00:52:55 2010 From: python-checkins at python.org (benjamin.peterson) Date: Sun, 8 Aug 2010 00:52:55 +0200 (CEST) Subject: [Python-checkins] r83791 - sandbox/trunk/2to3/lib2to3/fixes/fix_urllib.py Message-ID: <20100807225255.A6FD1EE981@mail.python.org> Author: benjamin.peterson Date: Sun Aug 8 00:52:55 2010 New Revision: 83791 Log: .get() is pointless here Modified: sandbox/trunk/2to3/lib2to3/fixes/fix_urllib.py Modified: sandbox/trunk/2to3/lib2to3/fixes/fix_urllib.py ============================================================================== --- sandbox/trunk/2to3/lib2to3/fixes/fix_urllib.py (original) +++ sandbox/trunk/2to3/lib2to3/fixes/fix_urllib.py Sun Aug 8 00:52:55 2010 @@ -119,7 +119,7 @@ # a dictionary for replacements, order matters modules = [] mod_dict = {} - members = results.get('members') + members = results['members'] for member in members: member = member.value # we only care about the actual members From python-checkins at python.org Sun Aug 8 01:31:27 2010 From: python-checkins at python.org (raymond.hettinger) Date: Sun, 8 Aug 2010 01:31:27 +0200 (CEST) Subject: [Python-checkins] r83792 - python/branches/py3k/Doc/library/heapq.rst Message-ID: <20100807233127.68C59EEA6F@mail.python.org> Author: raymond.hettinger Date: Sun Aug 8 01:31:27 2010 New Revision: 83792 Log: Document implementation notes for priority queues Modified: python/branches/py3k/Doc/library/heapq.rst Modified: python/branches/py3k/Doc/library/heapq.rst ============================================================================== --- python/branches/py3k/Doc/library/heapq.rst (original) +++ python/branches/py3k/Doc/library/heapq.rst Sun Aug 8 01:31:27 2010 @@ -6,6 +6,7 @@ .. moduleauthor:: Kevin O'Connor .. sectionauthor:: Guido van Rossum .. sectionauthor:: Fran?ois Pinard +.. sectionauthor:: Raymond Hettinger This module provides an implementation of the heap queue algorithm, also known as the priority queue algorithm. @@ -138,6 +139,67 @@ functions. +Priority Queue Implementation Notes +----------------------------------- + +A `priority queue `_ is common use +for a heap, and it presents several implementation challenges: + +* Sort stability: how do you get two tasks with equal priorities to be returned + in the order they were originally added? + +* Tuple comparison breaks for (priority, task) pairs if the priorities are equal + and the tasks do not have a default comparison order. + +* If the priority of a task changes, how do you move it to a new posistion in + the heap? + +* Or if a pending task needs to be deleted, how do you find it and remove it + from the queue? + +A solution to the first two challenges is to store entries as 3-element list +including the priority, an entry count, and the task. The entry count serves as +a tie-breaker so that two tasks with the same priority are returned in the order +they were added. And since no two entry counts are the same, the tuple +comparison will never attempt to directly compare two tasks. + +The remaining challenges revolve around finding a pending task and making +changes to its priority or removing it entirely. Finding a task can be done +with a dictionary pointing to an entry in the queue. + +Removing the entry or changing its priority is more difficult because it would +break the heap structure invariants. So, a possible solution is to mark an +entry as invalid and optionally add a new entry with the revised priority:: + + pq = [] # the priority queue list + counter = itertools.count(1) # unique sequence count + task_finder = {} # mapping of tasks to entries + INVALID = 0 # mark an entry as deleted + + def add_task(priority, task, count=None): + if count is None: + count = next(counter) + entry = [priority, count, task] + task_finder[task] = entry + heappush(pq, entry) + + def get_top_priority(): + while True: + priority, count, task = heappop(pq) + del task_finder[task] + if count is not INVALID: + return task + + def delete_task(task): + entry = task_finder[task] + entry[1] = INVALID + + def reprioritize(priority, task): + entry = task_finder[task] + add_task(priority, task, entry[1]) + entry[1] = INVALID + + Theory ------ From python-checkins at python.org Sun Aug 8 01:33:06 2010 From: python-checkins at python.org (raymond.hettinger) Date: Sun, 8 Aug 2010 01:33:06 +0200 (CEST) Subject: [Python-checkins] r83793 - python/branches/release31-maint/Doc/library/heapq.rst Message-ID: <20100807233306.00D67EEA7E@mail.python.org> Author: raymond.hettinger Date: Sun Aug 8 01:33:05 2010 New Revision: 83793 Log: Document implementation notes for priority queues Modified: python/branches/release31-maint/Doc/library/heapq.rst Modified: python/branches/release31-maint/Doc/library/heapq.rst ============================================================================== --- python/branches/release31-maint/Doc/library/heapq.rst (original) +++ python/branches/release31-maint/Doc/library/heapq.rst Sun Aug 8 01:33:05 2010 @@ -6,6 +6,7 @@ .. moduleauthor:: Kevin O'Connor .. sectionauthor:: Guido van Rossum .. sectionauthor:: Fran?ois Pinard +.. sectionauthor:: Raymond Hettinger This module provides an implementation of the heap queue algorithm, also known as the priority queue algorithm. @@ -138,6 +139,67 @@ functions. +Priority Queue Implementation Notes +----------------------------------- + +A `priority queue `_ is common use +for a heap, and it presents several implementation challenges: + +* Sort stability: how do you get two tasks with equal priorities to be returned + in the order they were originally added? + +* Tuple comparison breaks for (priority, task) pairs if the priorities are equal + and the tasks do not have a default comparison order. + +* If the priority of a task changes, how do you move it to a new posistion in + the heap? + +* Or if a pending task needs to be deleted, how do you find it and remove it + from the queue? + +A solution to the first two challenges is to store entries as 3-element list +including the priority, an entry count, and the task. The entry count serves as +a tie-breaker so that two tasks with the same priority are returned in the order +they were added. And since no two entry counts are the same, the tuple +comparison will never attempt to directly compare two tasks. + +The remaining challenges revolve around finding a pending task and making +changes to its priority or removing it entirely. Finding a task can be done +with a dictionary pointing to an entry in the queue. + +Removing the entry or changing its priority is more difficult because it would +break the heap structure invariants. So, a possible solution is to mark an +entry as invalid and optionally add a new entry with the revised priority:: + + pq = [] # the priority queue list + counter = itertools.count(1) # unique sequence count + task_finder = {} # mapping of tasks to entries + INVALID = 0 # mark an entry as deleted + + def add_task(priority, task, count=None): + if count is None: + count = next(counter) + entry = [priority, count, task] + task_finder[task] = entry + heappush(pq, entry) + + def get_top_priority(): + while True: + priority, count, task = heappop(pq) + del task_finder[task] + if count is not INVALID: + return task + + def delete_task(task): + entry = task_finder[task] + entry[1] = INVALID + + def reprioritize(priority, task): + entry = task_finder[task] + add_task(priority, task, entry[1]) + entry[1] = INVALID + + Theory ------ From python-checkins at python.org Sun Aug 8 01:35:52 2010 From: python-checkins at python.org (raymond.hettinger) Date: Sun, 8 Aug 2010 01:35:52 +0200 (CEST) Subject: [Python-checkins] r83794 - python/branches/release27-maint/Doc/library/heapq.rst Message-ID: <20100807233552.2D8A8EE9CD@mail.python.org> Author: raymond.hettinger Date: Sun Aug 8 01:35:52 2010 New Revision: 83794 Log: Document implementation notes for priority queues Modified: python/branches/release27-maint/Doc/library/heapq.rst Modified: python/branches/release27-maint/Doc/library/heapq.rst ============================================================================== --- python/branches/release27-maint/Doc/library/heapq.rst (original) +++ python/branches/release27-maint/Doc/library/heapq.rst Sun Aug 8 01:35:52 2010 @@ -6,6 +6,7 @@ .. moduleauthor:: Kevin O'Connor .. sectionauthor:: Guido van Rossum .. sectionauthor:: Fran?ois Pinard +.. sectionauthor:: Raymond Hettinger .. versionadded:: 2.3 @@ -151,6 +152,68 @@ functions. +Priority Queue Implementation Notes +----------------------------------- + +A `priority queue `_ is common use +for a heap, and it presents several implementation challenges: + +* Sort stability: how do you get two tasks with equal priorities to be returned + in the order they were originally added? + +* In the future with Python 3, tuple comparison breaks for (priority, task) + pairs if the priorities are equal and the tasks do not have a default + comparison order. + +* If the priority of a task changes, how do you move it to a new position in + the heap? + +* Or if a pending task needs to be deleted, how do you find it and remove it + from the queue? + +A solution to the first two challenges is to store entries as 3-element list +including the priority, an entry count, and the task. The entry count serves as +a tie-breaker so that two tasks with the same priority are returned in the order +they were added. And since no two entry counts are the same, the tuple +comparison will never attempt to directly compare two tasks. + +The remaining challenges revolve around finding a pending task and making +changes to its priority or removing it entirely. Finding a task can be done +with a dictionary pointing to an entry in the queue. + +Removing the entry or changing its priority is more difficult because it would +break the heap structure invariants. So, a possible solution is to mark an +entry as invalid and optionally add a new entry with the revised priority:: + + pq = [] # the priority queue list + counter = itertools.count(1) # unique sequence count + task_finder = {} # mapping of tasks to entries + INVALID = 0 # mark an entry as deleted + + def add_task(priority, task, count=None): + if count is None: + count = next(counter) + entry = [priority, count, task] + task_finder[task] = entry + heappush(pq, entry) + + def get_top_priority(): + while True: + priority, count, task = heappop(pq) + del task_finder[task] + if count is not INVALID: + return task + + def delete_task(task): + entry = task_finder[task] + entry[1] = INVALID + + def reprioritize(priority, task): + entry = task_finder[task] + add_task(priority, task, entry[1]) + entry[1] = INVALID + + Theory ------ From python-checkins at python.org Sun Aug 8 01:37:37 2010 From: python-checkins at python.org (raymond.hettinger) Date: Sun, 8 Aug 2010 01:37:37 +0200 (CEST) Subject: [Python-checkins] r83795 - python/branches/py3k/Doc/library/heapq.rst Message-ID: <20100807233737.5E74FEEA45@mail.python.org> Author: raymond.hettinger Date: Sun Aug 8 01:37:37 2010 New Revision: 83795 Log: Fix typo Modified: python/branches/py3k/Doc/library/heapq.rst Modified: python/branches/py3k/Doc/library/heapq.rst ============================================================================== --- python/branches/py3k/Doc/library/heapq.rst (original) +++ python/branches/py3k/Doc/library/heapq.rst Sun Aug 8 01:37:37 2010 @@ -151,7 +151,7 @@ * Tuple comparison breaks for (priority, task) pairs if the priorities are equal and the tasks do not have a default comparison order. -* If the priority of a task changes, how do you move it to a new posistion in +* If the priority of a task changes, how do you move it to a new position in the heap? * Or if a pending task needs to be deleted, how do you find it and remove it From python-checkins at python.org Sun Aug 8 01:38:16 2010 From: python-checkins at python.org (raymond.hettinger) Date: Sun, 8 Aug 2010 01:38:16 +0200 (CEST) Subject: [Python-checkins] r83796 - python/branches/release31-maint/Doc/library/heapq.rst Message-ID: <20100807233816.DD6F1EE9EE@mail.python.org> Author: raymond.hettinger Date: Sun Aug 8 01:38:16 2010 New Revision: 83796 Log: Fix typo Modified: python/branches/release31-maint/Doc/library/heapq.rst Modified: python/branches/release31-maint/Doc/library/heapq.rst ============================================================================== --- python/branches/release31-maint/Doc/library/heapq.rst (original) +++ python/branches/release31-maint/Doc/library/heapq.rst Sun Aug 8 01:38:16 2010 @@ -151,7 +151,7 @@ * Tuple comparison breaks for (priority, task) pairs if the priorities are equal and the tasks do not have a default comparison order. -* If the priority of a task changes, how do you move it to a new posistion in +* If the priority of a task changes, how do you move it to a new position in the heap? * Or if a pending task needs to be deleted, how do you find it and remove it From python-checkins at python.org Sun Aug 8 01:54:51 2010 From: python-checkins at python.org (benjamin.peterson) Date: Sun, 8 Aug 2010 01:54:51 +0200 (CEST) Subject: [Python-checkins] r83797 - in sandbox/trunk/2to3/lib2to3: fixer_util.py tests/test_util.py Message-ID: <20100807235451.540EEEEA4D@mail.python.org> Author: benjamin.peterson Date: Sun Aug 8 01:54:51 2010 New Revision: 83797 Log: add a function to find how a node is indented Modified: sandbox/trunk/2to3/lib2to3/fixer_util.py sandbox/trunk/2to3/lib2to3/tests/test_util.py Modified: sandbox/trunk/2to3/lib2to3/fixer_util.py ============================================================================== --- sandbox/trunk/2to3/lib2to3/fixer_util.py (original) +++ sandbox/trunk/2to3/lib2to3/fixer_util.py Sun Aug 8 01:54:51 2010 @@ -1,6 +1,8 @@ """Utility functions, node construction macros, etc.""" # Author: Collin Winter +from itertools import islice + # Local imports from .pgen2 import token from .pytree import Leaf, Node @@ -245,6 +247,16 @@ return False return True +def find_indentation(node): + """Find the indentation of *node*.""" + while node is not None: + if node.type == syms.suite and len(node.children) > 2: + indent = node.children[1] + if indent.type == token.INDENT: + return indent.value + node = node.parent + return u"" + ########################################################### ### The following functions are to find bindings in a suite ########################################################### Modified: sandbox/trunk/2to3/lib2to3/tests/test_util.py ============================================================================== --- sandbox/trunk/2to3/lib2to3/tests/test_util.py (original) +++ sandbox/trunk/2to3/lib2to3/tests/test_util.py Sun Aug 8 01:54:51 2010 @@ -575,3 +575,20 @@ node = parse('bar()') fixer_util.touch_import(None, "cgi", node) self.assertEqual(str(node), 'import cgi\nbar()\n\n') + +class Test_find_indentation(support.TestCase): + + def test_nothing(self): + fi = fixer_util.find_indentation + node = parse("node()") + self.assertEqual(fi(node), u"") + node = parse("") + self.assertEqual(fi(node), u"") + + def test_simple(self): + fi = fixer_util.find_indentation + node = parse("def f():\n x()") + self.assertEqual(fi(node), u"") + self.assertEqual(fi(node.children[0].children[4].children[2]), u" ") + node = parse("def f():\n x()\n y()") + self.assertEqual(fi(node.children[0].children[4].children[4]), u" ") From python-checkins at python.org Sun Aug 8 01:55:28 2010 From: python-checkins at python.org (benjamin.peterson) Date: Sun, 8 Aug 2010 01:55:28 +0200 (CEST) Subject: [Python-checkins] r83798 - in sandbox/trunk/2to3/lib2to3: fixes/fix_urllib.py tests/test_fixers.py Message-ID: <20100807235528.5A40EEEA5F@mail.python.org> Author: benjamin.peterson Date: Sun Aug 8 01:55:28 2010 New Revision: 83798 Log: when splitting import statements, use correct indentation #9386 Modified: sandbox/trunk/2to3/lib2to3/fixes/fix_urllib.py sandbox/trunk/2to3/lib2to3/tests/test_fixers.py Modified: sandbox/trunk/2to3/lib2to3/fixes/fix_urllib.py ============================================================================== --- sandbox/trunk/2to3/lib2to3/fixes/fix_urllib.py (original) +++ sandbox/trunk/2to3/lib2to3/fixes/fix_urllib.py Sun Aug 8 01:55:28 2010 @@ -7,7 +7,8 @@ # Local imports from .fix_imports import alternates, FixImports from .. import fixer_base -from ..fixer_util import Name, Comma, FromImport, Newline, attr_chain +from ..fixer_util import (Name, Comma, FromImport, Newline, attr_chain, + find_indentation) MAPPING = {'urllib': [ ('urllib.request', @@ -133,13 +134,17 @@ modules.append(change[0]) new_nodes = [] - for module in modules: + indentation = find_indentation(node) + for i, module in enumerate(modules): elts = mod_dict[module] names = [] for elt in elts[:-1]: names.extend([Name(elt, prefix=pref), Comma()]) names.append(Name(elts[-1], prefix=pref)) - new_nodes.append(FromImport(module, names)) + new = FromImport(module, names) + if i > 0: + new.prefix = indentation + new_nodes.append(new) if new_nodes: nodes = [] for new_node in new_nodes[:-1]: Modified: sandbox/trunk/2to3/lib2to3/tests/test_fixers.py ============================================================================== --- sandbox/trunk/2to3/lib2to3/tests/test_fixers.py (original) +++ sandbox/trunk/2to3/lib2to3/tests/test_fixers.py Sun Aug 8 01:55:28 2010 @@ -1818,6 +1818,19 @@ s = "from %s import *" % old self.warns_unchanged(s, "Cannot handle star imports") + def test_indented(self): + b = """ +def foo(): + from urllib import urlencode, urlopen +""" + a = """ +def foo(): + from urllib.parse import urlencode + from urllib.request import urlopen +""" + self.check(b, a) + + def test_import_module_usage(self): for old, changes in self.modules.items(): for new, members in changes: From python-checkins at python.org Sun Aug 8 01:57:43 2010 From: python-checkins at python.org (benjamin.peterson) Date: Sun, 8 Aug 2010 01:57:43 +0200 (CEST) Subject: [Python-checkins] r83799 - sandbox/trunk/2to3/lib2to3/fixer_util.py Message-ID: <20100807235743.8C1E3EE9D1@mail.python.org> Author: benjamin.peterson Date: Sun Aug 8 01:57:43 2010 New Revision: 83799 Log: double quotes Modified: sandbox/trunk/2to3/lib2to3/fixer_util.py Modified: sandbox/trunk/2to3/lib2to3/fixer_util.py ============================================================================== --- sandbox/trunk/2to3/lib2to3/fixer_util.py (original) +++ sandbox/trunk/2to3/lib2to3/fixer_util.py Sun Aug 8 01:57:43 2010 @@ -16,7 +16,7 @@ def KeywordArg(keyword, value): return Node(syms.argument, - [keyword, Leaf(token.EQUAL, u'='), value]) + [keyword, Leaf(token.EQUAL, u"="), value]) def LParen(): return Leaf(token.LPAR, u"(") @@ -78,9 +78,9 @@ def Subscript(index_node): """A numeric or string subscript""" - return Node(syms.trailer, [Leaf(token.LBRACE, u'['), + return Node(syms.trailer, [Leaf(token.LBRACE, u"["), index_node, - Leaf(token.RBRACE, u']')]) + Leaf(token.RBRACE, u"]")]) def String(string, prefix=None): """A string leaf""" @@ -122,9 +122,9 @@ # Pull the leaves out of their old tree leaf.remove() - children = [Leaf(token.NAME, u'from'), + children = [Leaf(token.NAME, u"from"), Leaf(token.NAME, package_name, prefix=u" "), - Leaf(token.NAME, u'import', prefix=u" "), + Leaf(token.NAME, u"import", prefix=u" "), Node(syms.import_as_names, name_leafs)] imp = Node(syms.import_from, children) return imp @@ -326,11 +326,11 @@ if package is None: import_ = Node(syms.import_name, [ - Leaf(token.NAME, u'import'), - Leaf(token.NAME, name, prefix=u' ') + Leaf(token.NAME, u"import"), + Leaf(token.NAME, name, prefix=u" ") ]) else: - import_ = FromImport(package, [Leaf(token.NAME, name, prefix=u' ')]) + import_ = FromImport(package, [Leaf(token.NAME, name, prefix=u" ")]) children = [import_, Newline()] root.insert_child(insert_pos, Node(syms.simple_stmt, children)) @@ -416,7 +416,7 @@ if package and unicode(node.children[1]).strip() != package: return None n = node.children[3] - if package and _find(u'as', n): + if package and _find(u"as", n): # See test_from_import_as for explanation return None elif n.type == syms.import_as_names and _find(name, n): From python-checkins at python.org Sun Aug 8 01:58:52 2010 From: python-checkins at python.org (benjamin.peterson) Date: Sun, 8 Aug 2010 01:58:52 +0200 (CEST) Subject: [Python-checkins] r83800 - sandbox/trunk/2to3/lib2to3/tests/test_fixers.py Message-ID: <20100807235852.EE28AEE9CD@mail.python.org> Author: benjamin.peterson Date: Sun Aug 8 01:58:52 2010 New Revision: 83800 Log: add another test Modified: sandbox/trunk/2to3/lib2to3/tests/test_fixers.py Modified: sandbox/trunk/2to3/lib2to3/tests/test_fixers.py ============================================================================== --- sandbox/trunk/2to3/lib2to3/tests/test_fixers.py (original) +++ sandbox/trunk/2to3/lib2to3/tests/test_fixers.py Sun Aug 8 01:58:52 2010 @@ -1829,6 +1829,18 @@ from urllib.request import urlopen """ self.check(b, a) + b = """ +def foo(): + other() + from urllib import urlencode, urlopen +""" + a = """ +def foo(): + other() + from urllib.parse import urlencode + from urllib.request import urlopen +""" + def test_import_module_usage(self): From python-checkins at python.org Sun Aug 8 02:02:10 2010 From: python-checkins at python.org (benjamin.peterson) Date: Sun, 8 Aug 2010 02:02:10 +0200 (CEST) Subject: [Python-checkins] r83801 - sandbox/trunk/2to3/lib2to3/fixes/fix_urllib.py Message-ID: <20100808000210.DC69DEEA65@mail.python.org> Author: benjamin.peterson Date: Sun Aug 8 02:02:10 2010 New Revision: 83801 Log: cleanup; style-nits Modified: sandbox/trunk/2to3/lib2to3/fixes/fix_urllib.py Modified: sandbox/trunk/2to3/lib2to3/fixes/fix_urllib.py ============================================================================== --- sandbox/trunk/2to3/lib2to3/fixes/fix_urllib.py (original) +++ sandbox/trunk/2to3/lib2to3/fixes/fix_urllib.py Sun Aug 8 02:02:10 2010 @@ -5,40 +5,40 @@ # Author: Nick Edds # Local imports -from .fix_imports import alternates, FixImports -from .. import fixer_base -from ..fixer_util import (Name, Comma, FromImport, Newline, attr_chain, - find_indentation) - -MAPPING = {'urllib': [ - ('urllib.request', - ['URLOpener', 'FancyURLOpener', 'urlretrieve', - '_urlopener', 'urlopen', 'urlcleanup', - 'pathname2url', 'url2pathname']), - ('urllib.parse', - ['quote', 'quote_plus', 'unquote', 'unquote_plus', - 'urlencode', 'splitattr', 'splithost', 'splitnport', - 'splitpasswd', 'splitport', 'splitquery', 'splittag', - 'splittype', 'splituser', 'splitvalue', ]), - ('urllib.error', - ['ContentTooShortError'])], - 'urllib2' : [ - ('urllib.request', - ['urlopen', 'install_opener', 'build_opener', - 'Request', 'OpenerDirector', 'BaseHandler', - 'HTTPDefaultErrorHandler', 'HTTPRedirectHandler', - 'HTTPCookieProcessor', 'ProxyHandler', - 'HTTPPasswordMgr', - 'HTTPPasswordMgrWithDefaultRealm', - 'AbstractBasicAuthHandler', - 'HTTPBasicAuthHandler', 'ProxyBasicAuthHandler', - 'AbstractDigestAuthHandler', - 'HTTPDigestAuthHandler', 'ProxyDigestAuthHandler', - 'HTTPHandler', 'HTTPSHandler', 'FileHandler', - 'FTPHandler', 'CacheFTPHandler', - 'UnknownHandler']), - ('urllib.error', - ['URLError', 'HTTPError']), +from lib2to3.fixes.fix_imports import alternates, FixImports +from lib2to3 import fixer_base +from lib2to3.fixer_util import (Name, Comma, FromImport, Newline, + find_indentation) + +MAPPING = {"urllib": [ + ("urllib.request", + ["URLOpener", "FancyURLOpener", "urlretrieve", + "_urlopener", "urlopen", "urlcleanup", + "pathname2url", "url2pathname"]), + ("urllib.parse", + ["quote", "quote_plus", "unquote", "unquote_plus", + "urlencode", "splitattr", "splithost", "splitnport", + "splitpasswd", "splitport", "splitquery", "splittag", + "splittype", "splituser", "splitvalue", ]), + ("urllib.error", + ["ContentTooShortError"])], + "urllib2" : [ + ("urllib.request", + ["urlopen", "install_opener", "build_opener", + "Request", "OpenerDirector", "BaseHandler", + "HTTPDefaultErrorHandler", "HTTPRedirectHandler", + "HTTPCookieProcessor", "ProxyHandler", + "HTTPPasswordMgr", + "HTTPPasswordMgrWithDefaultRealm", + "AbstractBasicAuthHandler", + "HTTPBasicAuthHandler", "ProxyBasicAuthHandler", + "AbstractDigestAuthHandler", + "HTTPDigestAuthHandler", "ProxyDigestAuthHandler", + "HTTPHandler", "HTTPSHandler", "FileHandler", + "FTPHandler", "CacheFTPHandler", + "UnknownHandler"]), + ("urllib.error", + ["URLError", "HTTPError"]), ] } @@ -79,7 +79,7 @@ import name with a comma separated list of its replacements. """ - import_mod = results.get('module') + import_mod = results.get("module") pref = import_mod.prefix names = [] @@ -95,9 +95,9 @@ the module to be imported from with the appropriate new module. """ - mod_member = results.get('mod_member') + mod_member = results.get("mod_member") pref = mod_member.prefix - member = results.get('member') + member = results.get("member") # Simple case with only a single member being imported if member: @@ -112,19 +112,18 @@ if new_name: mod_member.replace(Name(new_name, prefix=pref)) else: - self.cannot_convert(node, - 'This is an invalid module element') + self.cannot_convert(node, "This is an invalid module element") # Multiple members being imported else: # a dictionary for replacements, order matters modules = [] mod_dict = {} - members = results['members'] + members = results["members"] for member in members: member = member.value # we only care about the actual members - if member != u',': + if member != u",": for change in MAPPING[mod_member.value]: if member in change[1]: if change[0] in mod_dict: @@ -152,12 +151,12 @@ nodes.append(new_nodes[-1]) node.replace(nodes) else: - self.cannot_convert(node, 'All module elements are invalid') + self.cannot_convert(node, "All module elements are invalid") def transform_dot(self, node, results): """Transform for calls to module members in code.""" - module_dot = results.get('bare_with_attr') - member = results.get('member') + module_dot = results.get("bare_with_attr") + member = results.get("member") new_name = None if isinstance(member, list): member = member[0] @@ -169,17 +168,17 @@ module_dot.replace(Name(new_name, prefix=module_dot.prefix)) else: - self.cannot_convert(node, 'This is an invalid module element') + self.cannot_convert(node, "This is an invalid module element") def transform(self, node, results): - if results.get('module'): + if results.get("module"): self.transform_import(node, results) - elif results.get('mod_member'): + elif results.get("mod_member"): self.transform_member(node, results) - elif results.get('bare_with_attr'): + elif results.get("bare_with_attr"): self.transform_dot(node, results) # Renaming and star imports are not supported for these modules. - elif results.get('module_star'): - self.cannot_convert(node, 'Cannot handle star imports.') - elif results.get('module_as'): - self.cannot_convert(node, 'This module is now multiple modules') + elif results.get("module_star"): + self.cannot_convert(node, "Cannot handle star imports.") + elif results.get("module_as"): + self.cannot_convert(node, "This module is now multiple modules") From python-checkins at python.org Sun Aug 8 02:04:26 2010 From: python-checkins at python.org (benjamin.peterson) Date: Sun, 8 Aug 2010 02:04:26 +0200 (CEST) Subject: [Python-checkins] r83802 - in python/branches/py3k: Doc/documenting/building.rst Doc/howto/descriptor.rst Include/pytime.h Lib/distutils/tests/test_log.py Lib/test/mock_socket.py Lib/test/test_numeric_tower.py Lib/test/test_rlcompleter.py Lib/test/test_sched.py Lib/test/test_smtpd.py Lib/test/test_sndhdr.py Lib/test/test_sunau.py Lib/test/test_trace.py Lib/tkinter/__main__.py Modules/_time.c Modules/_time.h Python/pytime.c Message-ID: <20100808000426.95459FD43@mail.python.org> Author: benjamin.peterson Date: Sun Aug 8 02:04:26 2010 New Revision: 83802 Log: set svn:eol-style Modified: python/branches/py3k/Doc/documenting/building.rst (props changed) python/branches/py3k/Doc/howto/descriptor.rst (props changed) python/branches/py3k/Include/pytime.h (props changed) python/branches/py3k/Lib/distutils/tests/test_log.py (props changed) python/branches/py3k/Lib/test/mock_socket.py (props changed) python/branches/py3k/Lib/test/test_numeric_tower.py (props changed) python/branches/py3k/Lib/test/test_rlcompleter.py (props changed) python/branches/py3k/Lib/test/test_sched.py (props changed) python/branches/py3k/Lib/test/test_smtpd.py (props changed) python/branches/py3k/Lib/test/test_sndhdr.py (props changed) python/branches/py3k/Lib/test/test_sunau.py (props changed) python/branches/py3k/Lib/test/test_trace.py (props changed) python/branches/py3k/Lib/tkinter/__main__.py (props changed) python/branches/py3k/Modules/_time.c (props changed) python/branches/py3k/Modules/_time.h (props changed) python/branches/py3k/Python/pytime.c (props changed) From python-checkins at python.org Sun Aug 8 02:05:08 2010 From: python-checkins at python.org (benjamin.peterson) Date: Sun, 8 Aug 2010 02:05:08 +0200 (CEST) Subject: [Python-checkins] r83803 - sandbox/trunk/2to3/lib2to3/fixes/fix_urllib.py Message-ID: <20100808000508.9F9B7F45E@mail.python.org> Author: benjamin.peterson Date: Sun Aug 8 02:05:08 2010 New Revision: 83803 Log: slightly more explicit Modified: sandbox/trunk/2to3/lib2to3/fixes/fix_urllib.py Modified: sandbox/trunk/2to3/lib2to3/fixes/fix_urllib.py ============================================================================== --- sandbox/trunk/2to3/lib2to3/fixes/fix_urllib.py (original) +++ sandbox/trunk/2to3/lib2to3/fixes/fix_urllib.py Sun Aug 8 02:05:08 2010 @@ -134,16 +134,18 @@ new_nodes = [] indentation = find_indentation(node) - for i, module in enumerate(modules): + first = True + for module in modules: elts = mod_dict[module] names = [] for elt in elts[:-1]: names.extend([Name(elt, prefix=pref), Comma()]) names.append(Name(elts[-1], prefix=pref)) new = FromImport(module, names) - if i > 0: + if not first: new.prefix = indentation new_nodes.append(new) + first = False if new_nodes: nodes = [] for new_node in new_nodes[:-1]: From python-checkins at python.org Sun Aug 8 02:29:08 2010 From: python-checkins at python.org (raymond.hettinger) Date: Sun, 8 Aug 2010 02:29:08 +0200 (CEST) Subject: [Python-checkins] r83804 - python/branches/py3k/Doc/library/collections.rst Message-ID: <20100808002908.C708BEEA85@mail.python.org> Author: raymond.hettinger Date: Sun Aug 8 02:29:08 2010 New Revision: 83804 Log: Improve readability of collections docs by adding a summary table at the top and by list concrete classes before abstract base classes. Modified: python/branches/py3k/Doc/library/collections.rst Modified: python/branches/py3k/Doc/library/collections.rst ============================================================================== --- python/branches/py3k/Doc/library/collections.rst (original) +++ python/branches/py3k/Doc/library/collections.rst Sun Aug 8 02:29:08 2010 @@ -12,131 +12,24 @@ import itertools __name__ = '' -This module implements high-performance container datatypes. Currently, -there are four datatypes, :class:`Counter`, :class:`deque`, :class:`OrderedDict` and -:class:`defaultdict`, and one datatype factory function, :func:`namedtuple`. - -The specialized containers provided in this module provide alternatives -to Python's general purpose built-in containers, :class:`dict`, -:class:`list`, :class:`set`, and :class:`tuple`. - -In addition to containers, the collections module provides some ABCs -(abstract base classes) that can be used to test whether a class -provides a particular interface, for example, whether it is hashable or -a mapping. - -ABCs - abstract base classes ----------------------------- - -The collections module offers the following ABCs: - -========================= ===================== ====================== ==================================================== -ABC Inherits Abstract Methods Mixin Methods -========================= ===================== ====================== ==================================================== -:class:`Container` ``__contains__`` -:class:`Hashable` ``__hash__`` -:class:`Iterable` ``__iter__`` -:class:`Iterator` :class:`Iterable` ``__next__`` ``__iter__`` -:class:`Sized` ``__len__`` -:class:`Callable` ``__call__`` - -:class:`Sequence` :class:`Sized`, ``__getitem__`` ``__contains__``. ``__iter__``, ``__reversed__``. - :class:`Iterable`, ``index``, and ``count`` - :class:`Container` - -:class:`MutableSequence` :class:`Sequence` ``__setitem__`` Inherited Sequence methods and - ``__delitem__``, ``append``, ``reverse``, ``extend``, ``pop``, - and ``insert`` ``remove``, and ``__iadd__`` - -:class:`Set` :class:`Sized`, ``__le__``, ``__lt__``, ``__eq__``, ``__ne__``, - :class:`Iterable`, ``__gt__``, ``__ge__``, ``__and__``, ``__or__`` - :class:`Container` ``__sub__``, ``__xor__``, and ``isdisjoint`` - -:class:`MutableSet` :class:`Set` ``add`` and Inherited Set methods and - ``discard`` ``clear``, ``pop``, ``remove``, ``__ior__``, - ``__iand__``, ``__ixor__``, and ``__isub__`` - -:class:`Mapping` :class:`Sized`, ``__getitem__`` ``__contains__``, ``keys``, ``items``, ``values``, - :class:`Iterable`, ``get``, ``__eq__``, and ``__ne__`` - :class:`Container` - -:class:`MutableMapping` :class:`Mapping` ``__setitem__`` and Inherited Mapping methods and - ``__delitem__`` ``pop``, ``popitem``, ``clear``, ``update``, - and ``setdefault`` - - -:class:`MappingView` :class:`Sized` ``__len__`` -:class:`KeysView` :class:`MappingView`, ``__contains__``, - :class:`Set` ``__iter__`` -:class:`ItemsView` :class:`MappingView`, ``__contains__``, - :class:`Set` ``__iter__`` -:class:`ValuesView` :class:`MappingView` ``__contains__``, ``__iter__`` -========================= ===================== ====================== ==================================================== - -These ABCs allow us to ask classes or instances if they provide -particular functionality, for example:: - - size = None - if isinstance(myvar, collections.Sized): - size = len(myvar) - -Several of the ABCs are also useful as mixins that make it easier to develop -classes supporting container APIs. For example, to write a class supporting -the full :class:`Set` API, it only necessary to supply the three underlying -abstract methods: :meth:`__contains__`, :meth:`__iter__`, and :meth:`__len__`. -The ABC supplies the remaining methods such as :meth:`__and__` and -:meth:`isdisjoint` :: - - class ListBasedSet(collections.Set): - ''' Alternate set implementation favoring space over speed - and not requiring the set elements to be hashable. ''' - def __init__(self, iterable): - self.elements = lst = [] - for value in iterable: - if value not in lst: - lst.append(value) - def __iter__(self): - return iter(self.elements) - def __contains__(self, value): - return value in self.elements - def __len__(self): - return len(self.elements) - - s1 = ListBasedSet('abcdef') - s2 = ListBasedSet('defghi') - overlap = s1 & s2 # The __and__() method is supported automatically - -Notes on using :class:`Set` and :class:`MutableSet` as a mixin: - -(1) - Since some set operations create new sets, the default mixin methods need - a way to create new instances from an iterable. The class constructor is - assumed to have a signature in the form ``ClassName(iterable)``. - That assumption is factored-out to an internal classmethod called - :meth:`_from_iterable` which calls ``cls(iterable)`` to produce a new set. - If the :class:`Set` mixin is being used in a class with a different - constructor signature, you will need to override :meth:`from_iterable` - with a classmethod that can construct new instances from - an iterable argument. - -(2) - To override the comparisons (presumably for speed, as the - semantics are fixed), redefine :meth:`__le__` and - then the other operations will automatically follow suit. - -(3) - The :class:`Set` mixin provides a :meth:`_hash` method to compute a hash value - for the set; however, :meth:`__hash__` is not defined because not all sets - are hashable or immutable. To add set hashabilty using mixins, - inherit from both :meth:`Set` and :meth:`Hashable`, then define - ``__hash__ = Set._hash``. - -.. seealso:: - - * `OrderedSet recipe `_ for an - example built on :class:`MutableSet`. - - * For more about ABCs, see the :mod:`abc` module and :pep:`3119`. +This module implements specialized container datatypes providing alternatives to +Python's general purpose built-in containers, :class:`dict`, :class:`list`, +:class:`set`, and :class:`tuple`. + +===================== ==================================================================== +:func:`namedtuple` factory function for creating tuple subclasses with named fields +:class:`deque` list-like container with fast appends and pops on either end +:class:`Counter` dict subclass for counting hashable objects +:class:`OrderedDict` dict subclass that remembers the order entries were added +:class:`defaultdict` dict subclass that calls a factory function to supply missing values +:class:`UserDict` wrapper around dictionary objects for easier dict subclassing +:class:`UserList` wrapper around list objects for easier list subclassing +:class:`UserString` wrapper around string objects for easier string subclassing +===================== ==================================================================== + +In addition to the concrete container classes, the collections module provides +ABCs (abstract base classes) that can be used to test whether a class provides a +particular interface, for example, whether it is hashable or a mapping. :class:`Counter` objects @@ -1036,3 +929,117 @@ be an instance of :class:`bytes`, :class:`str`, :class:`UserString` (or a subclass) or an arbitrary sequence which can be converted into a string using the built-in :func:`str` function. + + +ABCs - abstract base classes +---------------------------- + +The collections module offers the following ABCs: + +========================= ===================== ====================== ==================================================== +ABC Inherits Abstract Methods Mixin Methods +========================= ===================== ====================== ==================================================== +:class:`Container` ``__contains__`` +:class:`Hashable` ``__hash__`` +:class:`Iterable` ``__iter__`` +:class:`Iterator` :class:`Iterable` ``__next__`` ``__iter__`` +:class:`Sized` ``__len__`` +:class:`Callable` ``__call__`` + +:class:`Sequence` :class:`Sized`, ``__getitem__`` ``__contains__``. ``__iter__``, ``__reversed__``. + :class:`Iterable`, ``index``, and ``count`` + :class:`Container` + +:class:`MutableSequence` :class:`Sequence` ``__setitem__`` Inherited Sequence methods and + ``__delitem__``, ``append``, ``reverse``, ``extend``, ``pop``, + and ``insert`` ``remove``, and ``__iadd__`` + +:class:`Set` :class:`Sized`, ``__le__``, ``__lt__``, ``__eq__``, ``__ne__``, + :class:`Iterable`, ``__gt__``, ``__ge__``, ``__and__``, ``__or__`` + :class:`Container` ``__sub__``, ``__xor__``, and ``isdisjoint`` + +:class:`MutableSet` :class:`Set` ``add`` and Inherited Set methods and + ``discard`` ``clear``, ``pop``, ``remove``, ``__ior__``, + ``__iand__``, ``__ixor__``, and ``__isub__`` + +:class:`Mapping` :class:`Sized`, ``__getitem__`` ``__contains__``, ``keys``, ``items``, ``values``, + :class:`Iterable`, ``get``, ``__eq__``, and ``__ne__`` + :class:`Container` + +:class:`MutableMapping` :class:`Mapping` ``__setitem__`` and Inherited Mapping methods and + ``__delitem__`` ``pop``, ``popitem``, ``clear``, ``update``, + and ``setdefault`` + + +:class:`MappingView` :class:`Sized` ``__len__`` +:class:`KeysView` :class:`MappingView`, ``__contains__``, + :class:`Set` ``__iter__`` +:class:`ItemsView` :class:`MappingView`, ``__contains__``, + :class:`Set` ``__iter__`` +:class:`ValuesView` :class:`MappingView` ``__contains__``, ``__iter__`` +========================= ===================== ====================== ==================================================== + +These ABCs allow us to ask classes or instances if they provide +particular functionality, for example:: + + size = None + if isinstance(myvar, collections.Sized): + size = len(myvar) + +Several of the ABCs are also useful as mixins that make it easier to develop +classes supporting container APIs. For example, to write a class supporting +the full :class:`Set` API, it only necessary to supply the three underlying +abstract methods: :meth:`__contains__`, :meth:`__iter__`, and :meth:`__len__`. +The ABC supplies the remaining methods such as :meth:`__and__` and +:meth:`isdisjoint` :: + + class ListBasedSet(collections.Set): + ''' Alternate set implementation favoring space over speed + and not requiring the set elements to be hashable. ''' + def __init__(self, iterable): + self.elements = lst = [] + for value in iterable: + if value not in lst: + lst.append(value) + def __iter__(self): + return iter(self.elements) + def __contains__(self, value): + return value in self.elements + def __len__(self): + return len(self.elements) + + s1 = ListBasedSet('abcdef') + s2 = ListBasedSet('defghi') + overlap = s1 & s2 # The __and__() method is supported automatically + +Notes on using :class:`Set` and :class:`MutableSet` as a mixin: + +(1) + Since some set operations create new sets, the default mixin methods need + a way to create new instances from an iterable. The class constructor is + assumed to have a signature in the form ``ClassName(iterable)``. + That assumption is factored-out to an internal classmethod called + :meth:`_from_iterable` which calls ``cls(iterable)`` to produce a new set. + If the :class:`Set` mixin is being used in a class with a different + constructor signature, you will need to override :meth:`from_iterable` + with a classmethod that can construct new instances from + an iterable argument. + +(2) + To override the comparisons (presumably for speed, as the + semantics are fixed), redefine :meth:`__le__` and + then the other operations will automatically follow suit. + +(3) + The :class:`Set` mixin provides a :meth:`_hash` method to compute a hash value + for the set; however, :meth:`__hash__` is not defined because not all sets + are hashable or immutable. To add set hashabilty using mixins, + inherit from both :meth:`Set` and :meth:`Hashable`, then define + ``__hash__ = Set._hash``. + +.. seealso:: + + * `OrderedSet recipe `_ for an + example built on :class:`MutableSet`. + + * For more about ABCs, see the :mod:`abc` module and :pep:`3119`. From python-checkins at python.org Sun Aug 8 02:32:32 2010 From: python-checkins at python.org (raymond.hettinger) Date: Sun, 8 Aug 2010 02:32:32 +0200 (CEST) Subject: [Python-checkins] r83805 - python/branches/release31-maint/Doc/library/collections.rst Message-ID: <20100808003232.716A1EEA04@mail.python.org> Author: raymond.hettinger Date: Sun Aug 8 02:32:32 2010 New Revision: 83805 Log: Backport doc readability improvements for the collections module. Modified: python/branches/release31-maint/Doc/library/collections.rst Modified: python/branches/release31-maint/Doc/library/collections.rst ============================================================================== --- python/branches/release31-maint/Doc/library/collections.rst (original) +++ python/branches/release31-maint/Doc/library/collections.rst Sun Aug 8 02:32:32 2010 @@ -12,131 +12,24 @@ import itertools __name__ = '' -This module implements high-performance container datatypes. Currently, -there are four datatypes, :class:`Counter`, :class:`deque`, :class:`OrderedDict` and -:class:`defaultdict`, and one datatype factory function, :func:`namedtuple`. - -The specialized containers provided in this module provide alternatives -to Python's general purpose built-in containers, :class:`dict`, -:class:`list`, :class:`set`, and :class:`tuple`. - -In addition to containers, the collections module provides some ABCs -(abstract base classes) that can be used to test whether a class -provides a particular interface, for example, whether it is hashable or -a mapping. - -ABCs - abstract base classes ----------------------------- - -The collections module offers the following ABCs: - -========================= ===================== ====================== ==================================================== -ABC Inherits Abstract Methods Mixin Methods -========================= ===================== ====================== ==================================================== -:class:`Container` ``__contains__`` -:class:`Hashable` ``__hash__`` -:class:`Iterable` ``__iter__`` -:class:`Iterator` :class:`Iterable` ``__next__`` ``__iter__`` -:class:`Sized` ``__len__`` -:class:`Callable` ``__call__`` - -:class:`Sequence` :class:`Sized`, ``__getitem__`` ``__contains__``. ``__iter__``, ``__reversed__``. - :class:`Iterable`, ``index``, and ``count`` - :class:`Container` - -:class:`MutableSequence` :class:`Sequence` ``__setitem__`` Inherited Sequence methods and - ``__delitem__``, ``append``, ``reverse``, ``extend``, ``pop``, - and ``insert`` ``remove``, and ``__iadd__`` - -:class:`Set` :class:`Sized`, ``__le__``, ``__lt__``, ``__eq__``, ``__ne__``, - :class:`Iterable`, ``__gt__``, ``__ge__``, ``__and__``, ``__or__`` - :class:`Container` ``__sub__``, ``__xor__``, and ``isdisjoint`` - -:class:`MutableSet` :class:`Set` ``add`` and Inherited Set methods and - ``discard`` ``clear``, ``pop``, ``remove``, ``__ior__``, - ``__iand__``, ``__ixor__``, and ``__isub__`` - -:class:`Mapping` :class:`Sized`, ``__getitem__`` ``__contains__``, ``keys``, ``items``, ``values``, - :class:`Iterable`, ``get``, ``__eq__``, and ``__ne__`` - :class:`Container` - -:class:`MutableMapping` :class:`Mapping` ``__setitem__`` and Inherited Mapping methods and - ``__delitem__`` ``pop``, ``popitem``, ``clear``, ``update``, - and ``setdefault`` - - -:class:`MappingView` :class:`Sized` ``__len__`` -:class:`KeysView` :class:`MappingView`, ``__contains__``, - :class:`Set` ``__iter__`` -:class:`ItemsView` :class:`MappingView`, ``__contains__``, - :class:`Set` ``__iter__`` -:class:`ValuesView` :class:`MappingView` ``__contains__``, ``__iter__`` -========================= ===================== ====================== ==================================================== - -These ABCs allow us to ask classes or instances if they provide -particular functionality, for example:: - - size = None - if isinstance(myvar, collections.Sized): - size = len(myvar) - -Several of the ABCs are also useful as mixins that make it easier to develop -classes supporting container APIs. For example, to write a class supporting -the full :class:`Set` API, it only necessary to supply the three underlying -abstract methods: :meth:`__contains__`, :meth:`__iter__`, and :meth:`__len__`. -The ABC supplies the remaining methods such as :meth:`__and__` and -:meth:`isdisjoint` :: - - class ListBasedSet(collections.Set): - ''' Alternate set implementation favoring space over speed - and not requiring the set elements to be hashable. ''' - def __init__(self, iterable): - self.elements = lst = [] - for value in iterable: - if value not in lst: - lst.append(value) - def __iter__(self): - return iter(self.elements) - def __contains__(self, value): - return value in self.elements - def __len__(self): - return len(self.elements) - - s1 = ListBasedSet('abcdef') - s2 = ListBasedSet('defghi') - overlap = s1 & s2 # The __and__() method is supported automatically - -Notes on using :class:`Set` and :class:`MutableSet` as a mixin: - -(1) - Since some set operations create new sets, the default mixin methods need - a way to create new instances from an iterable. The class constructor is - assumed to have a signature in the form ``ClassName(iterable)``. - That assumption is factored-out to an internal classmethod called - :meth:`_from_iterable` which calls ``cls(iterable)`` to produce a new set. - If the :class:`Set` mixin is being used in a class with a different - constructor signature, you will need to override :meth:`from_iterable` - with a classmethod that can construct new instances from - an iterable argument. - -(2) - To override the comparisons (presumably for speed, as the - semantics are fixed), redefine :meth:`__le__` and - then the other operations will automatically follow suit. - -(3) - The :class:`Set` mixin provides a :meth:`_hash` method to compute a hash value - for the set; however, :meth:`__hash__` is not defined because not all sets - are hashable or immutable. To add set hashabilty using mixins, - inherit from both :meth:`Set` and :meth:`Hashable`, then define - ``__hash__ = Set._hash``. - -.. seealso:: - - * `OrderedSet recipe `_ for an - example built on :class:`MutableSet`. - - * For more about ABCs, see the :mod:`abc` module and :pep:`3119`. +This module implements specialized container datatypes providing alternatives to +Python's general purpose built-in containers, :class:`dict`, :class:`list`, +:class:`set`, and :class:`tuple`. + +===================== ==================================================================== +:func:`namedtuple` factory function for creating tuple subclasses with named fields +:class:`deque` list-like container with fast appends and pops on either end +:class:`Counter` dict subclass for counting hashable objects +:class:`OrderedDict` dict subclass that remembers the order entries were added +:class:`defaultdict` dict subclass that calls a factory function to supply missing values +:class:`UserDict` wrapper around dictionary objects for easier dict subclassing +:class:`UserList` wrapper around list objects for easier list subclassing +:class:`UserString` wrapper around string objects for easier string subclassing +===================== ==================================================================== + +In addition to the concrete container classes, the collections module provides +ABCs (abstract base classes) that can be used to test whether a class provides a +particular interface, for example, whether it is hashable or a mapping. :class:`Counter` objects @@ -1009,3 +902,117 @@ be an instance of :class:`bytes`, :class:`str`, :class:`UserString` (or a subclass) or an arbitrary sequence which can be converted into a string using the built-in :func:`str` function. + + +ABCs - abstract base classes +---------------------------- + +The collections module offers the following ABCs: + +========================= ===================== ====================== ==================================================== +ABC Inherits Abstract Methods Mixin Methods +========================= ===================== ====================== ==================================================== +:class:`Container` ``__contains__`` +:class:`Hashable` ``__hash__`` +:class:`Iterable` ``__iter__`` +:class:`Iterator` :class:`Iterable` ``__next__`` ``__iter__`` +:class:`Sized` ``__len__`` +:class:`Callable` ``__call__`` + +:class:`Sequence` :class:`Sized`, ``__getitem__`` ``__contains__``. ``__iter__``, ``__reversed__``. + :class:`Iterable`, ``index``, and ``count`` + :class:`Container` + +:class:`MutableSequence` :class:`Sequence` ``__setitem__`` Inherited Sequence methods and + ``__delitem__``, ``append``, ``reverse``, ``extend``, ``pop``, + and ``insert`` ``remove``, and ``__iadd__`` + +:class:`Set` :class:`Sized`, ``__le__``, ``__lt__``, ``__eq__``, ``__ne__``, + :class:`Iterable`, ``__gt__``, ``__ge__``, ``__and__``, ``__or__`` + :class:`Container` ``__sub__``, ``__xor__``, and ``isdisjoint`` + +:class:`MutableSet` :class:`Set` ``add`` and Inherited Set methods and + ``discard`` ``clear``, ``pop``, ``remove``, ``__ior__``, + ``__iand__``, ``__ixor__``, and ``__isub__`` + +:class:`Mapping` :class:`Sized`, ``__getitem__`` ``__contains__``, ``keys``, ``items``, ``values``, + :class:`Iterable`, ``get``, ``__eq__``, and ``__ne__`` + :class:`Container` + +:class:`MutableMapping` :class:`Mapping` ``__setitem__`` and Inherited Mapping methods and + ``__delitem__`` ``pop``, ``popitem``, ``clear``, ``update``, + and ``setdefault`` + + +:class:`MappingView` :class:`Sized` ``__len__`` +:class:`KeysView` :class:`MappingView`, ``__contains__``, + :class:`Set` ``__iter__`` +:class:`ItemsView` :class:`MappingView`, ``__contains__``, + :class:`Set` ``__iter__`` +:class:`ValuesView` :class:`MappingView` ``__contains__``, ``__iter__`` +========================= ===================== ====================== ==================================================== + +These ABCs allow us to ask classes or instances if they provide +particular functionality, for example:: + + size = None + if isinstance(myvar, collections.Sized): + size = len(myvar) + +Several of the ABCs are also useful as mixins that make it easier to develop +classes supporting container APIs. For example, to write a class supporting +the full :class:`Set` API, it only necessary to supply the three underlying +abstract methods: :meth:`__contains__`, :meth:`__iter__`, and :meth:`__len__`. +The ABC supplies the remaining methods such as :meth:`__and__` and +:meth:`isdisjoint` :: + + class ListBasedSet(collections.Set): + ''' Alternate set implementation favoring space over speed + and not requiring the set elements to be hashable. ''' + def __init__(self, iterable): + self.elements = lst = [] + for value in iterable: + if value not in lst: + lst.append(value) + def __iter__(self): + return iter(self.elements) + def __contains__(self, value): + return value in self.elements + def __len__(self): + return len(self.elements) + + s1 = ListBasedSet('abcdef') + s2 = ListBasedSet('defghi') + overlap = s1 & s2 # The __and__() method is supported automatically + +Notes on using :class:`Set` and :class:`MutableSet` as a mixin: + +(1) + Since some set operations create new sets, the default mixin methods need + a way to create new instances from an iterable. The class constructor is + assumed to have a signature in the form ``ClassName(iterable)``. + That assumption is factored-out to an internal classmethod called + :meth:`_from_iterable` which calls ``cls(iterable)`` to produce a new set. + If the :class:`Set` mixin is being used in a class with a different + constructor signature, you will need to override :meth:`from_iterable` + with a classmethod that can construct new instances from + an iterable argument. + +(2) + To override the comparisons (presumably for speed, as the + semantics are fixed), redefine :meth:`__le__` and + then the other operations will automatically follow suit. + +(3) + The :class:`Set` mixin provides a :meth:`_hash` method to compute a hash value + for the set; however, :meth:`__hash__` is not defined because not all sets + are hashable or immutable. To add set hashabilty using mixins, + inherit from both :meth:`Set` and :meth:`Hashable`, then define + ``__hash__ = Set._hash``. + +.. seealso:: + + * `OrderedSet recipe `_ for an + example built on :class:`MutableSet`. + + * For more about ABCs, see the :mod:`abc` module and :pep:`3119`. From python-checkins at python.org Sun Aug 8 02:35:04 2010 From: python-checkins at python.org (raymond.hettinger) Date: Sun, 8 Aug 2010 02:35:04 +0200 (CEST) Subject: [Python-checkins] r83806 - python/branches/release27-maint/Doc/library/collections.rst Message-ID: <20100808003504.2A17FEEA8D@mail.python.org> Author: raymond.hettinger Date: Sun Aug 8 02:35:03 2010 New Revision: 83806 Log: Backport improvements to doc readability for the collections module. Modified: python/branches/release27-maint/Doc/library/collections.rst Modified: python/branches/release27-maint/Doc/library/collections.rst ============================================================================== --- python/branches/release27-maint/Doc/library/collections.rst (original) +++ python/branches/release27-maint/Doc/library/collections.rst Sun Aug 8 02:35:03 2010 @@ -15,13 +15,17 @@ import itertools __name__ = '' -This module implements high-performance container datatypes. Currently, -there are four datatypes, :class:`Counter`, :class:`deque`, :class:`OrderedDict` and -:class:`defaultdict`, and one datatype factory function, :func:`namedtuple`. - -The specialized containers provided in this module provide alternatives -to Python's general purpose built-in containers, :class:`dict`, -:class:`list`, :class:`set`, and :class:`tuple`. +This module implements specialized container datatypes providing alternatives to +Python's general purpose built-in containers, :class:`dict`, :class:`list`, +:class:`set`, and :class:`tuple`. + +===================== ==================================================================== +:func:`namedtuple` factory function for creating tuple subclasses with named fields +:class:`deque` list-like container with fast appends and pops on either end +:class:`Counter` dict subclass for counting hashable objects +:class:`OrderedDict` dict subclass that remembers the order entries were added +:class:`defaultdict` dict subclass that calls a factory function to supply missing values +===================== ==================================================================== .. versionchanged:: 2.4 Added :class:`deque`. @@ -41,120 +45,6 @@ a mapping. -ABCs - abstract base classes ----------------------------- - -The collections module offers the following ABCs: - -========================= ===================== ====================== ==================================================== -ABC Inherits Abstract Methods Mixin Methods -========================= ===================== ====================== ==================================================== -:class:`Container` ``__contains__`` -:class:`Hashable` ``__hash__`` -:class:`Iterable` ``__iter__`` -:class:`Iterator` :class:`Iterable` ``next`` ``__iter__`` -:class:`Sized` ``__len__`` -:class:`Callable` ``__call__`` - -:class:`Sequence` :class:`Sized`, ``__getitem__`` ``__contains__``. ``__iter__``, ``__reversed__``. - :class:`Iterable`, ``index``, and ``count`` - :class:`Container` - -:class:`MutableSequence` :class:`Sequence` ``__setitem__`` Inherited Sequence methods and - ``__delitem__``, ``append``, ``reverse``, ``extend``, ``pop``, - and ``insert`` ``remove``, and ``__iadd__`` - -:class:`Set` :class:`Sized`, ``__le__``, ``__lt__``, ``__eq__``, ``__ne__``, - :class:`Iterable`, ``__gt__``, ``__ge__``, ``__and__``, ``__or__`` - :class:`Container` ``__sub__``, ``__xor__``, and ``isdisjoint`` - -:class:`MutableSet` :class:`Set` ``add`` and Inherited Set methods and - ``discard`` ``clear``, ``pop``, ``remove``, ``__ior__``, - ``__iand__``, ``__ixor__``, and ``__isub__`` - -:class:`Mapping` :class:`Sized`, ``__getitem__`` ``__contains__``, ``keys``, ``items``, ``values``, - :class:`Iterable`, ``get``, ``__eq__``, and ``__ne__`` - :class:`Container` - -:class:`MutableMapping` :class:`Mapping` ``__setitem__`` and Inherited Mapping methods and - ``__delitem__`` ``pop``, ``popitem``, ``clear``, ``update``, - and ``setdefault`` - - -:class:`MappingView` :class:`Sized` ``__len__`` -:class:`KeysView` :class:`MappingView`, ``__contains__``, - :class:`Set` ``__iter__`` -:class:`ItemsView` :class:`MappingView`, ``__contains__``, - :class:`Set` ``__iter__`` -:class:`ValuesView` :class:`MappingView` ``__contains__``, ``__iter__`` -========================= ===================== ====================== ==================================================== - -These ABCs allow us to ask classes or instances if they provide -particular functionality, for example:: - - size = None - if isinstance(myvar, collections.Sized): - size = len(myvar) - -Several of the ABCs are also useful as mixins that make it easier to develop -classes supporting container APIs. For example, to write a class supporting -the full :class:`Set` API, it only necessary to supply the three underlying -abstract methods: :meth:`__contains__`, :meth:`__iter__`, and :meth:`__len__`. -The ABC supplies the remaining methods such as :meth:`__and__` and -:meth:`isdisjoint` :: - - class ListBasedSet(collections.Set): - ''' Alternate set implementation favoring space over speed - and not requiring the set elements to be hashable. ''' - def __init__(self, iterable): - self.elements = lst = [] - for value in iterable: - if value not in lst: - lst.append(value) - def __iter__(self): - return iter(self.elements) - def __contains__(self, value): - return value in self.elements - def __len__(self): - return len(self.elements) - - s1 = ListBasedSet('abcdef') - s2 = ListBasedSet('defghi') - overlap = s1 & s2 # The __and__() method is supported automatically - -Notes on using :class:`Set` and :class:`MutableSet` as a mixin: - -(1) - Since some set operations create new sets, the default mixin methods need - a way to create new instances from an iterable. The class constructor is - assumed to have a signature in the form ``ClassName(iterable)``. - That assumption is factored-out to an internal classmethod called - :meth:`_from_iterable` which calls ``cls(iterable)`` to produce a new set. - If the :class:`Set` mixin is being used in a class with a different - constructor signature, you will need to override :meth:`from_iterable` - with a classmethod that can construct new instances from - an iterable argument. - -(2) - To override the comparisons (presumably for speed, as the - semantics are fixed), redefine :meth:`__le__` and - then the other operations will automatically follow suit. - -(3) - The :class:`Set` mixin provides a :meth:`_hash` method to compute a hash value - for the set; however, :meth:`__hash__` is not defined because not all sets - are hashable or immutable. To add set hashabilty using mixins, - inherit from both :meth:`Set` and :meth:`Hashable`, then define - ``__hash__ = Set._hash``. - -.. seealso:: - - * `OrderedSet recipe `_ for an - example built on :class:`MutableSet`. - - * For more about ABCs, see the :mod:`abc` module and :pep:`3119`. - - :class:`Counter` objects ------------------------ @@ -970,3 +860,117 @@ if key in self: del self[key] OrderedDict.__setitem__(self, key, value) + + +ABCs - abstract base classes +---------------------------- + +The collections module offers the following ABCs: + +========================= ===================== ====================== ==================================================== +ABC Inherits Abstract Methods Mixin Methods +========================= ===================== ====================== ==================================================== +:class:`Container` ``__contains__`` +:class:`Hashable` ``__hash__`` +:class:`Iterable` ``__iter__`` +:class:`Iterator` :class:`Iterable` ``next`` ``__iter__`` +:class:`Sized` ``__len__`` +:class:`Callable` ``__call__`` + +:class:`Sequence` :class:`Sized`, ``__getitem__`` ``__contains__``. ``__iter__``, ``__reversed__``. + :class:`Iterable`, ``index``, and ``count`` + :class:`Container` + +:class:`MutableSequence` :class:`Sequence` ``__setitem__`` Inherited Sequence methods and + ``__delitem__``, ``append``, ``reverse``, ``extend``, ``pop``, + and ``insert`` ``remove``, and ``__iadd__`` + +:class:`Set` :class:`Sized`, ``__le__``, ``__lt__``, ``__eq__``, ``__ne__``, + :class:`Iterable`, ``__gt__``, ``__ge__``, ``__and__``, ``__or__`` + :class:`Container` ``__sub__``, ``__xor__``, and ``isdisjoint`` + +:class:`MutableSet` :class:`Set` ``add`` and Inherited Set methods and + ``discard`` ``clear``, ``pop``, ``remove``, ``__ior__``, + ``__iand__``, ``__ixor__``, and ``__isub__`` + +:class:`Mapping` :class:`Sized`, ``__getitem__`` ``__contains__``, ``keys``, ``items``, ``values``, + :class:`Iterable`, ``get``, ``__eq__``, and ``__ne__`` + :class:`Container` + +:class:`MutableMapping` :class:`Mapping` ``__setitem__`` and Inherited Mapping methods and + ``__delitem__`` ``pop``, ``popitem``, ``clear``, ``update``, + and ``setdefault`` + + +:class:`MappingView` :class:`Sized` ``__len__`` +:class:`KeysView` :class:`MappingView`, ``__contains__``, + :class:`Set` ``__iter__`` +:class:`ItemsView` :class:`MappingView`, ``__contains__``, + :class:`Set` ``__iter__`` +:class:`ValuesView` :class:`MappingView` ``__contains__``, ``__iter__`` +========================= ===================== ====================== ==================================================== + +These ABCs allow us to ask classes or instances if they provide +particular functionality, for example:: + + size = None + if isinstance(myvar, collections.Sized): + size = len(myvar) + +Several of the ABCs are also useful as mixins that make it easier to develop +classes supporting container APIs. For example, to write a class supporting +the full :class:`Set` API, it only necessary to supply the three underlying +abstract methods: :meth:`__contains__`, :meth:`__iter__`, and :meth:`__len__`. +The ABC supplies the remaining methods such as :meth:`__and__` and +:meth:`isdisjoint` :: + + class ListBasedSet(collections.Set): + ''' Alternate set implementation favoring space over speed + and not requiring the set elements to be hashable. ''' + def __init__(self, iterable): + self.elements = lst = [] + for value in iterable: + if value not in lst: + lst.append(value) + def __iter__(self): + return iter(self.elements) + def __contains__(self, value): + return value in self.elements + def __len__(self): + return len(self.elements) + + s1 = ListBasedSet('abcdef') + s2 = ListBasedSet('defghi') + overlap = s1 & s2 # The __and__() method is supported automatically + +Notes on using :class:`Set` and :class:`MutableSet` as a mixin: + +(1) + Since some set operations create new sets, the default mixin methods need + a way to create new instances from an iterable. The class constructor is + assumed to have a signature in the form ``ClassName(iterable)``. + That assumption is factored-out to an internal classmethod called + :meth:`_from_iterable` which calls ``cls(iterable)`` to produce a new set. + If the :class:`Set` mixin is being used in a class with a different + constructor signature, you will need to override :meth:`from_iterable` + with a classmethod that can construct new instances from + an iterable argument. + +(2) + To override the comparisons (presumably for speed, as the + semantics are fixed), redefine :meth:`__le__` and + then the other operations will automatically follow suit. + +(3) + The :class:`Set` mixin provides a :meth:`_hash` method to compute a hash value + for the set; however, :meth:`__hash__` is not defined because not all sets + are hashable or immutable. To add set hashabilty using mixins, + inherit from both :meth:`Set` and :meth:`Hashable`, then define + ``__hash__ = Set._hash``. + +.. seealso:: + + * `OrderedSet recipe `_ for an + example built on :class:`MutableSet`. + + * For more about ABCs, see the :mod:`abc` module and :pep:`3119`. From python-checkins at python.org Sun Aug 8 02:56:52 2010 From: python-checkins at python.org (raymond.hettinger) Date: Sun, 8 Aug 2010 02:56:52 +0200 (CEST) Subject: [Python-checkins] r83807 - in python/branches/release31-maint: Doc/library/functools.rst Lib/functools.py Lib/test/test_functools.py Misc/ACKS Misc/NEWS Message-ID: <20100808005652.8E4ACEEA89@mail.python.org> Author: raymond.hettinger Date: Sun Aug 8 02:56:52 2010 New Revision: 83807 Log: Issue 8814: functools.wraps() did not copy __annotations__. Modified: python/branches/release31-maint/Doc/library/functools.rst python/branches/release31-maint/Lib/functools.py python/branches/release31-maint/Lib/test/test_functools.py python/branches/release31-maint/Misc/ACKS python/branches/release31-maint/Misc/NEWS Modified: python/branches/release31-maint/Doc/library/functools.rst ============================================================================== --- python/branches/release31-maint/Doc/library/functools.rst (original) +++ python/branches/release31-maint/Doc/library/functools.rst Sun Aug 8 02:56:52 2010 @@ -66,9 +66,9 @@ attributes of the wrapper function are updated with the corresponding attributes from the original function. The default values for these arguments are the module level constants *WRAPPER_ASSIGNMENTS* (which assigns to the wrapper - function's *__name__*, *__module__* and *__doc__*, the documentation string) and - *WRAPPER_UPDATES* (which updates the wrapper function's *__dict__*, i.e. the - instance dictionary). + function's *__name__*, *__module__*, *__annotations__* and *__doc__*, the + documentation string) and *WRAPPER_UPDATES* (which updates the wrapper + function's *__dict__*, i.e. the instance dictionary). The main intended use for this function is in :term:`decorator` functions which wrap the decorated function and return the wrapper. If the wrapper function is Modified: python/branches/release31-maint/Lib/functools.py ============================================================================== --- python/branches/release31-maint/Lib/functools.py (original) +++ python/branches/release31-maint/Lib/functools.py Sun Aug 8 02:56:52 2010 @@ -12,7 +12,7 @@ # update_wrapper() and wraps() are tools to help write # wrapper functions that can handle naive introspection -WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__doc__') +WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__doc__', '__annotations__') WRAPPER_UPDATES = ('__dict__',) def update_wrapper(wrapper, wrapped, @@ -30,7 +30,8 @@ function (defaults to functools.WRAPPER_UPDATES) """ for attr in assigned: - setattr(wrapper, attr, getattr(wrapped, attr)) + if hasattr(wrapped, attr): + setattr(wrapper, attr, getattr(wrapped, attr)) for attr in updated: getattr(wrapper, attr).update(getattr(wrapped, attr, {})) # Return the wrapper so this can be used as a decorator via partial() Modified: python/branches/release31-maint/Lib/test/test_functools.py ============================================================================== --- python/branches/release31-maint/Lib/test/test_functools.py (original) +++ python/branches/release31-maint/Lib/test/test_functools.py Sun Aug 8 02:56:52 2010 @@ -181,17 +181,19 @@ self.assertTrue(wrapped_attr[key] is wrapper_attr[key]) def test_default_update(self): - def f(): + def f(a:'This is a new annotation'): """This is a test""" pass f.attr = 'This is also a test' - def wrapper(): + def wrapper(b:'This is the prior annotation'): pass functools.update_wrapper(wrapper, f) self.check_wrapper(wrapper, f) self.assertEqual(wrapper.__name__, 'f') self.assertEqual(wrapper.__doc__, 'This is a test') self.assertEqual(wrapper.attr, 'This is also a test') + self.assertEqual(wrapper.__annotations__['a'], 'This is a new annotation') + self.assertNotIn('b', wrapper.__annotations__) def test_no_update(self): def f(): @@ -204,6 +206,7 @@ self.check_wrapper(wrapper, f, (), ()) self.assertEqual(wrapper.__name__, 'wrapper') self.assertEqual(wrapper.__doc__, None) + self.assertEqual(wrapper.__annotations__, {}) self.assertFalse(hasattr(wrapper, 'attr')) def test_selective_update(self): @@ -230,6 +233,7 @@ functools.update_wrapper(wrapper, max) self.assertEqual(wrapper.__name__, 'max') self.assertTrue(wrapper.__doc__.startswith('max(')) + self.assertEqual(wrapper.__annotations__, {}) class TestWraps(TestUpdateWrapper): Modified: python/branches/release31-maint/Misc/ACKS ============================================================================== --- python/branches/release31-maint/Misc/ACKS (original) +++ python/branches/release31-maint/Misc/ACKS Sun Aug 8 02:56:52 2010 @@ -144,6 +144,7 @@ Nick Coghlan Josh Cogliati Dave Cole +Terrence Cole Benjamin Collar Jeffery Collins Paul Colomiets Modified: python/branches/release31-maint/Misc/NEWS ============================================================================== --- python/branches/release31-maint/Misc/NEWS (original) +++ python/branches/release31-maint/Misc/NEWS Sun Aug 8 02:56:52 2010 @@ -12,6 +12,10 @@ Core and Builtins ----------------- +- Issue #8814: function annotations (the ``__annotations__`` attribute) + are now included in the set of attributes copied by default by + functools.wraps and functools.update_wrapper. Patch by Terrence Cole. + - Issue #83755: Implicit set-to-frozenset conversion was not thread-safe. - Issue #9416: Fix some issues with complex formatting where the From python-checkins at python.org Sun Aug 8 03:13:42 2010 From: python-checkins at python.org (raymond.hettinger) Date: Sun, 8 Aug 2010 03:13:42 +0200 (CEST) Subject: [Python-checkins] r83808 - in python/branches/py3k: Doc/library/collections.rst Lib/collections.py Lib/test/test_collections.py Misc/NEWS Message-ID: <20100808011342.92ADDEE99B@mail.python.org> Author: raymond.hettinger Date: Sun Aug 8 03:13:42 2010 New Revision: 83808 Log: Issue #9507: Named tuple repr will now automatically display the right name in a tuple subclass. Modified: python/branches/py3k/Doc/library/collections.rst python/branches/py3k/Lib/collections.py python/branches/py3k/Lib/test/test_collections.py python/branches/py3k/Misc/NEWS Modified: python/branches/py3k/Doc/library/collections.rst ============================================================================== --- python/branches/py3k/Doc/library/collections.rst (original) +++ python/branches/py3k/Doc/library/collections.rst Sun Aug 8 03:13:42 2010 @@ -605,7 +605,7 @@ def __repr__(self): 'Return a nicely formatted representation string' - return 'Point(x=%r, y=%r)' % self + return self.__class__.__name__ + '(x=%r, y=%r)' % self def _asdict(self): 'Return a new OrderedDict which maps field names to their values' Modified: python/branches/py3k/Lib/collections.py ============================================================================== --- python/branches/py3k/Lib/collections.py (original) +++ python/branches/py3k/Lib/collections.py Sun Aug 8 03:13:42 2010 @@ -240,7 +240,7 @@ return result \n def __repr__(self): 'Return a nicely formatted representation string' - return '%(typename)s(%(reprtxt)s)' %% self \n + return self.__class__.__name__ + '(%(reprtxt)s)' %% self \n def _asdict(self): 'Return a new OrderedDict which maps field names to their values' return OrderedDict(zip(self._fields, self)) \n Modified: python/branches/py3k/Lib/test/test_collections.py ============================================================================== --- python/branches/py3k/Lib/test/test_collections.py (original) +++ python/branches/py3k/Lib/test/test_collections.py Sun Aug 8 03:13:42 2010 @@ -218,6 +218,16 @@ # test __getnewargs__ self.assertEqual(t.__getnewargs__(), values) + def test_repr(self): + with support.captured_stdout() as template: + A = namedtuple('A', 'x', verbose=True) + self.assertEqual(repr(A(1)), 'A(x=1)') + # repr should show the name of the subclass + class B(A): + pass + self.assertEqual(repr(B(1)), 'B(x=1)') + + class ABCTestCase(unittest.TestCase): def validate_abstract_methods(self, abc, *names): Modified: python/branches/py3k/Misc/NEWS ============================================================================== --- python/branches/py3k/Misc/NEWS (original) +++ python/branches/py3k/Misc/NEWS Sun Aug 8 03:13:42 2010 @@ -24,6 +24,9 @@ Extensions ---------- +- Issue #9507: Named tuple repr will now automatically display the right + name in a tuple subclass. + - Issue #9324: Add parameter validation to signal.signal on Windows in order to prevent crashes. From python-checkins at python.org Sun Aug 8 03:30:45 2010 From: python-checkins at python.org (raymond.hettinger) Date: Sun, 8 Aug 2010 03:30:45 +0200 (CEST) Subject: [Python-checkins] r83809 - python/branches/py3k/Doc/tutorial/datastructures.rst Message-ID: <20100808013045.42640EEA5F@mail.python.org> Author: raymond.hettinger Date: Sun Aug 8 03:30:45 2010 New Revision: 83809 Log: Issue #4570: Clean-up tutorial example Modified: python/branches/py3k/Doc/tutorial/datastructures.rst Modified: python/branches/py3k/Doc/tutorial/datastructures.rst ============================================================================== --- python/branches/py3k/Doc/tutorial/datastructures.rst (original) +++ python/branches/py3k/Doc/tutorial/datastructures.rst Sun Aug 8 03:30:45 2010 @@ -377,16 +377,12 @@ Here is a brief demonstration:: - >>> basket = ['apple', 'orange', 'apple', 'pear', 'orange', 'banana'] - >>> fruit = set(basket) # create a set without duplicates - >>> fruit - {'orange', 'pear', 'apple', 'banana'} - >>> fruit = {'orange', 'apple'} # {} syntax is equivalent to [] for lists - >>> fruit - {'orange', 'apple'} - >>> 'orange' in fruit # fast membership testing + >>> basket = {'apple', 'orange', 'apple', 'pear', 'orange', 'banana'} + >>> print(basket) # show that duplicates have been removed + {'orange', 'bananna', 'pear', 'apple'} + >>> 'orange' in basket # fast membership testing True - >>> 'crabgrass' in fruit + >>> 'crabgrass' in basket False >>> # Demonstrate set operations on unique letters from two words From python-checkins at python.org Sun Aug 8 03:32:07 2010 From: python-checkins at python.org (raymond.hettinger) Date: Sun, 8 Aug 2010 03:32:07 +0200 (CEST) Subject: [Python-checkins] r83810 - python/branches/release31-maint/Doc/tutorial/datastructures.rst Message-ID: <20100808013207.1B05EEEA26@mail.python.org> Author: raymond.hettinger Date: Sun Aug 8 03:32:06 2010 New Revision: 83810 Log: Issue #4570: Clean-up tutorial example Modified: python/branches/release31-maint/Doc/tutorial/datastructures.rst Modified: python/branches/release31-maint/Doc/tutorial/datastructures.rst ============================================================================== --- python/branches/release31-maint/Doc/tutorial/datastructures.rst (original) +++ python/branches/release31-maint/Doc/tutorial/datastructures.rst Sun Aug 8 03:32:06 2010 @@ -377,16 +377,12 @@ Here is a brief demonstration:: - >>> basket = ['apple', 'orange', 'apple', 'pear', 'orange', 'banana'] - >>> fruit = set(basket) # create a set without duplicates - >>> fruit - {'orange', 'pear', 'apple', 'banana'} - >>> fruit = {'orange', 'apple'} # {} syntax is equivalent to [] for lists - >>> fruit - {'orange', 'apple'} - >>> 'orange' in fruit # fast membership testing + >>> basket = {'apple', 'orange', 'apple', 'pear', 'orange', 'banana'} + >>> print(basket) # show that duplicates have been removed + {'orange', 'bananna', 'pear', 'apple'} + >>> 'orange' in basket # fast membership testing True - >>> 'crabgrass' in fruit + >>> 'crabgrass' in basket False >>> # Demonstrate set operations on unique letters from two words From solipsis at pitrou.net Sun Aug 8 05:03:40 2010 From: solipsis at pitrou.net (solipsis at pitrou.net) Date: Sun, 08 Aug 2010 05:03:40 +0200 Subject: [Python-checkins] Daily py3k reference leaks (r83795): sum=0 Message-ID: py3k results for svn r83795 (hg cset 02d2879170dc) -------------------------------------------------- Command line was: ['./python', '-m', 'test.regrtest', '-uall', '-R', '3:3:/home/antoine/py3k/refleaks/reflogxkIgRq', '-x'] From python-checkins at python.org Sun Aug 8 05:56:44 2010 From: python-checkins at python.org (benjamin.peterson) Date: Sun, 8 Aug 2010 05:56:44 +0200 (CEST) Subject: [Python-checkins] r83811 - in sandbox/trunk/2to3/lib2to3: pytree.py tests/test_pytree.py Message-ID: <20100808035644.BAC42EE9DE@mail.python.org> Author: benjamin.peterson Date: Sun Aug 8 05:56:44 2010 New Revision: 83811 Log: Fix node.pre_order() to call the right method on its children. This was a rather tragic copy-paste error. Modified: sandbox/trunk/2to3/lib2to3/pytree.py sandbox/trunk/2to3/lib2to3/tests/test_pytree.py Modified: sandbox/trunk/2to3/lib2to3/pytree.py ============================================================================== --- sandbox/trunk/2to3/lib2to3/pytree.py (original) +++ sandbox/trunk/2to3/lib2to3/pytree.py Sun Aug 8 05:56:44 2010 @@ -286,7 +286,7 @@ """Return a pre-order iterator for the tree.""" yield self for child in self.children: - for node in child.post_order(): + for node in child.pre_order(): yield node def _prefix_getter(self): Modified: sandbox/trunk/2to3/lib2to3/tests/test_pytree.py ============================================================================== --- sandbox/trunk/2to3/lib2to3/tests/test_pytree.py (original) +++ sandbox/trunk/2to3/lib2to3/tests/test_pytree.py Sun Aug 8 05:56:44 2010 @@ -181,14 +181,18 @@ def test_post_order(self): l1 = pytree.Leaf(100, "foo") l2 = pytree.Leaf(100, "bar") - n1 = pytree.Node(1000, [l1, l2]) - self.assertEqual(list(n1.post_order()), [l1, l2, n1]) + l3 = pytree.Leaf(100, "fooey") + c1 = pytree.Node(1000, [l1, l2]) + n1 = pytree.Node(1000, [c1, l3]) + self.assertEqual(list(n1.post_order()), [l1, l2, c1, l3, n1]) def test_pre_order(self): l1 = pytree.Leaf(100, "foo") l2 = pytree.Leaf(100, "bar") - n1 = pytree.Node(1000, [l1, l2]) - self.assertEqual(list(n1.pre_order()), [n1, l1, l2]) + l3 = pytree.Leaf(100, "fooey") + c1 = pytree.Node(1000, [l1, l2]) + n1 = pytree.Node(1000, [c1, l3]) + self.assertEqual(list(n1.pre_order()), [n1, c1, l1, l2, l3]) def test_changed(self): l1 = pytree.Leaf(100, "f") From python-checkins at python.org Sun Aug 8 11:50:44 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:44 +0200 Subject: [Python-checkins] distutils2: removed commented out code Message-ID: tarek.ziade pushed 9830198e7d24 to distutils2: http://hg.python.org/distutils2/rev/9830198e7d24 changeset: 390:9830198e7d24 parent: 387:ba37b166e4f9 user: Yannick Gingras date: Thu Jul 29 21:44:20 2010 -0400 summary: removed commented out code files: src/distutils2/dist.py diff --git a/src/distutils2/dist.py b/src/distutils2/dist.py --- a/src/distutils2/dist.py +++ b/src/distutils2/dist.py @@ -152,10 +152,6 @@ # object in a sneaky and underhanded (but efficient!) way. self.metadata = DistributionMetadata() - #for basename in self.metadata._METHOD_BASENAMES: - # method_name = "get_" + basename - # setattr(self, method_name, getattr(self.metadata, method_name)) - # 'cmdclass' maps command names to class objects, so we # can 1) quickly figure out which class to instantiate when # we need to create a new command object, and 2) have a way -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:44 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:44 +0200 Subject: [Python-checkins] distutils2: Automated merge with https://bitbucket.org/mtlpython/distutils2 Message-ID: tarek.ziade pushed 611d144bff64 to distutils2: http://hg.python.org/distutils2/rev/611d144bff64 changeset: 389:611d144bff64 parent: 387:ba37b166e4f9 parent: 388:4e592a276c7b user: Nicolas Cadou date: Thu Jul 29 21:42:30 2010 -0400 summary: Automated merge with https://bitbucket.org/mtlpython/distutils2 files: src/distutils2/tests/test_metadata.py diff --git a/src/distutils2/metadata.py b/src/distutils2/metadata.py --- a/src/distutils2/metadata.py +++ b/src/distutils2/metadata.py @@ -203,10 +203,6 @@ def _write_field(self, file, name, value): file.write('%s: %s\n' % (name, value)) - def _write_list(self, file, name, values): - for value in values: - self._write_field(file, name, value) - def _encode_field(self, value): if isinstance(value, unicode): return value.encode(PKG_INFO_ENCODING) diff --git a/src/distutils2/tests/test_metadata.py b/src/distutils2/tests/test_metadata.py --- a/src/distutils2/tests/test_metadata.py +++ b/src/distutils2/tests/test_metadata.py @@ -1,6 +1,7 @@ """Tests for distutils.command.bdist.""" import os import sys +import tempfile from StringIO import StringIO from distutils2.metadata import (DistributionMetadata, _interpret, @@ -62,17 +63,12 @@ PKG_INFO = os.path.join(os.path.dirname(__file__), 'PKG-INFO') metadata = DistributionMetadata(PKG_INFO) - res = StringIO() + res = tempfile.NamedTemporaryFile() metadata.write_file(res) - res.seek(0) - res = res.read() - f = open(PKG_INFO) - try: - # XXX this is not used - wanted = f.read() - finally: - f.close() - self.assertTrue('Keywords: keyring,password,crypt' in res) + res.flush() + res = DistributionMetadata(res.name) + for k in metadata.keys(): + self.assertTrue(metadata[k] == res[k]) def test_metadata_markers(self): # see if we can be platform-aware -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:44 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:44 +0200 Subject: [Python-checkins] distutils2: Removed unecessary tempfile use Message-ID: tarek.ziade pushed 3c815a5fa448 to distutils2: http://hg.python.org/distutils2/rev/3c815a5fa448 changeset: 393:3c815a5fa448 user: Nicolas Cadou date: Fri Jul 30 00:25:29 2010 -0400 summary: Removed unecessary tempfile use files: src/distutils2/tests/test_metadata.py diff --git a/src/distutils2/tests/test_metadata.py b/src/distutils2/tests/test_metadata.py --- a/src/distutils2/tests/test_metadata.py +++ b/src/distutils2/tests/test_metadata.py @@ -1,7 +1,6 @@ """Tests for distutils.command.bdist.""" import os import sys -import tempfile from StringIO import StringIO from distutils2.metadata import (DistributionMetadata, _interpret, @@ -63,10 +62,11 @@ PKG_INFO = os.path.join(os.path.dirname(__file__), 'PKG-INFO') metadata = DistributionMetadata(PKG_INFO) - res = tempfile.NamedTemporaryFile() - metadata.write_file(res) - res.flush() - res = DistributionMetadata(res.name) + out = StringIO() + metadata.write_file(out) + out.seek(0) + res = DistributionMetadata() + res.read_file(out) for k in metadata.keys(): self.assertTrue(metadata[k] == res[k]) -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:44 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:44 +0200 Subject: [Python-checkins] distutils2: Automated merge with ssh://hg@bitbucket.org/mtlpython/distutils2 Message-ID: tarek.ziade pushed daeecc30870d to distutils2: http://hg.python.org/distutils2/rev/daeecc30870d changeset: 391:daeecc30870d parent: 389:611d144bff64 parent: 390:9830198e7d24 user: Yannick Gingras date: Thu Jul 29 21:46:33 2010 -0400 summary: Automated merge with ssh://hg at bitbucket.org/mtlpython/distutils2 files: diff --git a/src/distutils2/dist.py b/src/distutils2/dist.py --- a/src/distutils2/dist.py +++ b/src/distutils2/dist.py @@ -152,10 +152,6 @@ # object in a sneaky and underhanded (but efficient!) way. self.metadata = DistributionMetadata() - #for basename in self.metadata._METHOD_BASENAMES: - # method_name = "get_" + basename - # setattr(self, method_name, getattr(self.metadata, method_name)) - # 'cmdclass' maps command names to class objects, so we # can 1) quickly figure out which class to instantiate when # we need to create a new command object, and 2) have a way -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:44 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:44 +0200 Subject: [Python-checkins] distutils2: Improved code coverage Message-ID: tarek.ziade pushed db89914133c6 to distutils2: http://hg.python.org/distutils2/rev/db89914133c6 changeset: 395:db89914133c6 parent: 393:3c815a5fa448 user: Nicolas Cadou date: Fri Jul 30 01:18:01 2010 -0400 summary: Improved code coverage files: src/distutils2/tests/test_metadata.py diff --git a/src/distutils2/tests/test_metadata.py b/src/distutils2/tests/test_metadata.py --- a/src/distutils2/tests/test_metadata.py +++ b/src/distutils2/tests/test_metadata.py @@ -78,6 +78,8 @@ metadata = DistributionMetadata(platform_dependent=True) metadata.read_file(StringIO(content)) self.assertEqual(metadata['Requires-Dist'], ['bar']) + metadata['Name'] = "baz; sys.platform == 'blah'" + self.assertEquals(metadata['Name'], None) # test with context context = {'sys.platform': 'okook'} @@ -207,6 +209,10 @@ metadata = DistributionMetadata() metadata['Version'] = 'rr' metadata['Requires-dist'] = ['Foo (a)'] + if metadata.docutils_support: + missing, warnings = metadata.check() + self.assertEqual(len(warnings), 2) + metadata.docutils_support = False missing, warnings = metadata.check() self.assertEqual(missing, ['Name', 'Home-page']) self.assertEqual(len(warnings), 2) -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:44 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:44 +0200 Subject: [Python-checkins] distutils2: Improved metadata read/write test and removed unused function Message-ID: tarek.ziade pushed 4e592a276c7b to distutils2: http://hg.python.org/distutils2/rev/4e592a276c7b changeset: 388:4e592a276c7b parent: 379:da7378fa803a user: Nicolas Cadou date: Thu Jul 29 21:42:18 2010 -0400 summary: Improved metadata read/write test and removed unused function files: src/distutils2/metadata.py, src/distutils2/tests/test_metadata.py diff --git a/src/distutils2/metadata.py b/src/distutils2/metadata.py --- a/src/distutils2/metadata.py +++ b/src/distutils2/metadata.py @@ -203,10 +203,6 @@ def _write_field(self, file, name, value): file.write('%s: %s\n' % (name, value)) - def _write_list(self, file, name, values): - for value in values: - self._write_field(file, name, value) - def _encode_field(self, value): if isinstance(value, unicode): return value.encode(PKG_INFO_ENCODING) diff --git a/src/distutils2/tests/test_metadata.py b/src/distutils2/tests/test_metadata.py --- a/src/distutils2/tests/test_metadata.py +++ b/src/distutils2/tests/test_metadata.py @@ -1,6 +1,7 @@ """Tests for distutils.command.bdist.""" import os import sys +import tempfile from StringIO import StringIO from distutils2.metadata import (DistributionMetadata, _interpret, @@ -61,17 +62,12 @@ PKG_INFO = os.path.join(os.path.dirname(__file__), 'PKG-INFO') metadata = DistributionMetadata(PKG_INFO) - res = StringIO() + res = tempfile.NamedTemporaryFile() metadata.write_file(res) - res.seek(0) - res = res.read() - f = open(PKG_INFO) - try: - # XXX this is not used - wanted = f.read() - finally: - f.close() - self.assertTrue('Keywords: keyring,password,crypt' in res) + res.flush() + res = DistributionMetadata(res.name) + for k in metadata.keys(): + self.assertTrue(metadata[k] == res[k]) def test_metadata_markers(self): # see if we can be platform-aware -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:44 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:44 +0200 Subject: [Python-checkins] distutils2: changed some encoding declarations to their canonical names, which happens to Message-ID: tarek.ziade pushed 689ec6a55b14 to distutils2: http://hg.python.org/distutils2/rev/689ec6a55b14 changeset: 392:689ec6a55b14 user: Yannick Gingras date: Fri Jul 30 00:16:41 2010 -0400 summary: changed some encoding declarations to their canonical names, which happens to please Emacs files: src/distutils2/tests/conversions/02_after.py, src/distutils2/tests/conversions/02_before.py, src/distutils2/tests/test_dist.py, src/distutils2/tests/test_register.py, src/distutils2/tests/test_upload.py diff --git a/src/distutils2/tests/conversions/02_after.py b/src/distutils2/tests/conversions/02_after.py --- a/src/distutils2/tests/conversions/02_after.py +++ b/src/distutils2/tests/conversions/02_after.py @@ -1,4 +1,4 @@ -# -*- encoding: utf8 -*- +# -*- encoding: utf-8 -*- import sys import os from distutils2.core import setup, Extension diff --git a/src/distutils2/tests/conversions/02_before.py b/src/distutils2/tests/conversions/02_before.py --- a/src/distutils2/tests/conversions/02_before.py +++ b/src/distutils2/tests/conversions/02_before.py @@ -1,4 +1,4 @@ -# -*- encoding: utf8 -*- +# -*- encoding: utf-8 -*- import sys import os from distutils.core import setup, Extension diff --git a/src/distutils2/tests/test_dist.py b/src/distutils2/tests/test_dist.py --- a/src/distutils2/tests/test_dist.py +++ b/src/distutils2/tests/test_dist.py @@ -1,4 +1,4 @@ -# -*- coding: utf8 -*- +# -*- coding: utf-8 -*- """Tests for distutils2.dist.""" import os diff --git a/src/distutils2/tests/test_register.py b/src/distutils2/tests/test_register.py --- a/src/distutils2/tests/test_register.py +++ b/src/distutils2/tests/test_register.py @@ -1,5 +1,5 @@ +# -*- encoding: utf-8 -*- """Tests for distutils.command.register.""" -# -*- encoding: utf8 -*- import sys import os import getpass diff --git a/src/distutils2/tests/test_upload.py b/src/distutils2/tests/test_upload.py --- a/src/distutils2/tests/test_upload.py +++ b/src/distutils2/tests/test_upload.py @@ -1,5 +1,5 @@ +# -*- encoding: utf-8 -*- """Tests for distutils.command.upload.""" -# -*- encoding: utf8 -*- import os import sys -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:44 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:44 +0200 Subject: [Python-checkins] distutils2: Add fixme comment about dubious return value Message-ID: tarek.ziade pushed 2b3bd798734a to distutils2: http://hg.python.org/distutils2/rev/2b3bd798734a changeset: 397:2b3bd798734a parent: 395:db89914133c6 user: ?ric Araujo date: Fri Jul 30 07:28:52 2010 +0200 summary: Add fixme comment about dubious return value files: src/distutils2/tests/test_metadata.py diff --git a/src/distutils2/tests/test_metadata.py b/src/distutils2/tests/test_metadata.py --- a/src/distutils2/tests/test_metadata.py +++ b/src/distutils2/tests/test_metadata.py @@ -79,6 +79,8 @@ metadata.read_file(StringIO(content)) self.assertEqual(metadata['Requires-Dist'], ['bar']) metadata['Name'] = "baz; sys.platform == 'blah'" + # FIXME is None or 'UNKNOWN' correct here? + # where is that documented? self.assertEquals(metadata['Name'], None) # test with context -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:44 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:44 +0200 Subject: [Python-checkins] distutils2: Branch merge Message-ID: tarek.ziade pushed bf63d656f732 to distutils2: http://hg.python.org/distutils2/rev/bf63d656f732 changeset: 398:bf63d656f732 parent: 397:2b3bd798734a parent: 396:6dd1d6de357c user: ?ric Araujo date: Fri Jul 30 07:29:06 2010 +0200 summary: Branch merge files: diff --git a/src/distutils2/dist.py b/src/distutils2/dist.py --- a/src/distutils2/dist.py +++ b/src/distutils2/dist.py @@ -148,8 +148,7 @@ # Store the distribution meta-data (name, version, author, and so # forth) in a separate object -- we're getting to have enough # information here (and enough command-line options) that it's - # worth it. Also delegate 'get_XXX()' methods to the 'metadata' - # object in a sneaky and underhanded (but efficient!) way. + # worth it. self.metadata = DistributionMetadata() # 'cmdclass' maps command names to class objects, so we diff --git a/src/runtests-cov.py b/src/runtests-cov.py --- a/src/runtests-cov.py +++ b/src/runtests-cov.py @@ -8,6 +8,7 @@ from os.path import dirname, islink, realpath from optparse import OptionParser + def ignore_prefixes(module): """ Return a list of prefixes to ignore in the coverage report if we want to completely skip `module`. @@ -16,16 +17,17 @@ # distributions, such a Ubuntu, really like to build link farm in # /usr/lib in order to save a few bytes on the disk. dirnames = [dirname(module.__file__)] - + pymod = module.__file__.rstrip("c") if islink(pymod): dirnames.append(dirname(realpath(pymod))) return dirnames + def parse_opts(): parser = OptionParser(usage="%prog [OPTIONS]", description="run the distutils2 unittests") - + parser.add_option("-q", "--quiet", help="do not print verbose messages", action="store_true", default=False) parser.add_option("-c", "--coverage", action="store_true", default=False, @@ -36,10 +38,11 @@ default=False, help=("Show line numbers of statements in each module " "that weren't executed.")) - + opts, args = parser.parse_args() return opts, args + def coverage_report(opts): import coverage from distutils2.tests.support import unittest @@ -89,6 +92,7 @@ return ret + def run_tests(verbose): import distutils2.tests from distutils2.tests import run_unittest, reap_children, TestFailed @@ -108,12 +112,11 @@ finally: reap_children() + if __name__ == "__main__": try: from distutils2.tests.support import unittest except ImportError: sys.stderr.write('Error: You have to install unittest2') sys.exit(1) - sys.exit(test_main()) - diff --git a/src/runtests.py b/src/runtests.py --- a/src/runtests.py +++ b/src/runtests.py @@ -1,9 +1,12 @@ +#!/usr/bin/env python """Tests for distutils2. The tests for distutils2 are defined in the distutils2.tests package. """ + import sys + def test_main(): import distutils2.tests from distutils2.tests import run_unittest, reap_children, TestFailed @@ -23,12 +26,11 @@ finally: reap_children() + if __name__ == "__main__": try: from distutils2.tests.support import unittest except ImportError: sys.stderr.write('Error: You have to install unittest2') sys.exit(1) - sys.exit(test_main()) - -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:44 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:44 +0200 Subject: [Python-checkins] distutils2: Remove obsolete comment forgotten in 9830198e7d24 Message-ID: tarek.ziade pushed 28671dbbf3d0 to distutils2: http://hg.python.org/distutils2/rev/28671dbbf3d0 changeset: 394:28671dbbf3d0 user: ?ric Araujo date: Fri Jul 30 07:16:43 2010 +0200 summary: Remove obsolete comment forgotten in 9830198e7d24 files: src/distutils2/dist.py diff --git a/src/distutils2/dist.py b/src/distutils2/dist.py --- a/src/distutils2/dist.py +++ b/src/distutils2/dist.py @@ -148,8 +148,7 @@ # Store the distribution meta-data (name, version, author, and so # forth) in a separate object -- we're getting to have enough # information here (and enough command-line options) that it's - # worth it. Also delegate 'get_XXX()' methods to the 'metadata' - # object in a sneaky and underhanded (but efficient!) way. + # worth it. self.metadata = DistributionMetadata() # 'cmdclass' maps command names to class objects, so we -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:45 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:45 +0200 Subject: [Python-checkins] distutils2: Don?t test with python2.6 -3 to avoid noise from the stdlib. Message-ID: tarek.ziade pushed b811547a41e0 to distutils2: http://hg.python.org/distutils2/rev/b811547a41e0 changeset: 407:b811547a41e0 user: ?ric Araujo date: Sat Jul 31 14:06:49 2010 +0200 summary: Don?t test with python2.6 -3 to avoid noise from the stdlib. files: src/tests.sh diff --git a/src/tests.sh b/src/tests.sh --- a/src/tests.sh +++ b/src/tests.sh @@ -23,7 +23,7 @@ fi echo -n "Running tests for Python 2.6... " -python2.6 -Wd -bb -3 runtests.py -q 2> /dev/null +python2.6 -Wd runtests.py -q 2> /dev/null if [ $? -ne 0 ];then echo "Failed" exit 1 -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:45 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:45 +0200 Subject: [Python-checkins] distutils2: Fix one test that didn?t make sense (module/package confusion) Message-ID: tarek.ziade pushed fab35063ec35 to distutils2: http://hg.python.org/distutils2/rev/fab35063ec35 changeset: 400:fab35063ec35 user: ?ric Araujo date: Sun Jul 11 23:06:34 2010 +0200 summary: Fix one test that didn?t make sense (module/package confusion) files: src/distutils2/tests/test_install_lib.py diff --git a/src/distutils2/tests/test_install_lib.py b/src/distutils2/tests/test_install_lib.py --- a/src/distutils2/tests/test_install_lib.py +++ b/src/distutils2/tests/test_install_lib.py @@ -57,9 +57,8 @@ # setting up a dist environment cmd.compile = cmd.optimize = 1 cmd.install_dir = pkg_dir - f = os.path.join(pkg_dir, 'foo.py') - self.write_file(f, '# python file') - cmd.distribution.py_modules = [pkg_dir] + f = os.path.join(pkg_dir, '__init__.py') + self.write_file(f, '# python package') cmd.distribution.ext_modules = [Extension('foo', ['xxx'])] cmd.distribution.packages = [pkg_dir] cmd.distribution.script_name = 'setup.py' @@ -74,9 +73,8 @@ # setting up a dist environment cmd.compile = cmd.optimize = 1 cmd.install_dir = pkg_dir - f = os.path.join(pkg_dir, 'foo.py') - self.write_file(f, '# python file') - cmd.distribution.py_modules = [pkg_dir] + f = os.path.join(pkg_dir, '__init__.py') + self.write_file(f, '# python package') cmd.distribution.ext_modules = [Extension('foo', ['xxx'])] cmd.distribution.packages = [pkg_dir] cmd.distribution.script_name = 'setup.py' -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:45 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:45 +0200 Subject: [Python-checkins] distutils2: Remove unsafe mktemp use Message-ID: tarek.ziade pushed b7ecfb5b716f to distutils2: http://hg.python.org/distutils2/rev/b7ecfb5b716f changeset: 406:b7ecfb5b716f user: ?ric Araujo date: Sat Jul 31 13:58:32 2010 +0200 summary: Remove unsafe mktemp use files: src/distutils2/command/bdist_wininst.py diff --git a/src/distutils2/command/bdist_wininst.py b/src/distutils2/command/bdist_wininst.py --- a/src/distutils2/command/bdist_wininst.py +++ b/src/distutils2/command/bdist_wininst.py @@ -177,8 +177,8 @@ # And make an archive relative to the root of the # pseudo-installation tree. - from tempfile import mktemp - archive_basename = mktemp() + from tempfile import NamedTemporaryFile + archive_basename = NamedTemporaryFile().name fullname = self.distribution.get_fullname() arcname = self.make_archive(archive_basename, "zip", root_dir=self.bdist_dir) -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:45 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:45 +0200 Subject: [Python-checkins] distutils2: ove DummyCommand to group the three mixins in the source Message-ID: tarek.ziade pushed d2a49bdc5098 to distutils2: http://hg.python.org/distutils2/rev/d2a49bdc5098 changeset: 405:d2a49bdc5098 user: ?ric Araujo date: Sat Jul 31 13:54:16 2010 +0200 summary: ove DummyCommand to group the three mixins in the source files: src/distutils2/tests/support.py diff --git a/src/distutils2/tests/support.py b/src/distutils2/tests/support.py --- a/src/distutils2/tests/support.py +++ b/src/distutils2/tests/support.py @@ -159,21 +159,6 @@ return pkg_dir, dist -class DummyCommand(object): - """Class to store options for retrieval via set_undefined_options(). - - Useful for mocking one dependency command in the tests for another - command, see e.g. the dummy build command in test_build_scripts. - """ - - def __init__(self, **kwargs): - for kw, val in kwargs.iteritems(): - setattr(self, kw, val) - - def ensure_finalized(self): - pass - - class EnvironGuard(object): """TestCase-compatible mixin to save and restore the environment.""" @@ -191,3 +176,18 @@ del os.environ[key] super(EnvironGuard, self).tearDown() + + +class DummyCommand(object): + """Class to store options for retrieval via set_undefined_options(). + + Useful for mocking one dependency command in the tests for another + command, see e.g. the dummy build command in test_build_scripts. + """ + + def __init__(self, **kwargs): + for kw, val in kwargs.iteritems(): + setattr(self, kw, val) + + def ensure_finalized(self): + pass -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:44 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:44 +0200 Subject: [Python-checkins] distutils2: Very minor edits to runtests* to remove diff noise between them Message-ID: tarek.ziade pushed 6dd1d6de357c to distutils2: http://hg.python.org/distutils2/rev/6dd1d6de357c changeset: 396:6dd1d6de357c parent: 394:28671dbbf3d0 user: ?ric Araujo date: Fri Jul 30 07:24:21 2010 +0200 summary: Very minor edits to runtests* to remove diff noise between them files: src/runtests-cov.py, src/runtests.py diff --git a/src/runtests-cov.py b/src/runtests-cov.py --- a/src/runtests-cov.py +++ b/src/runtests-cov.py @@ -8,6 +8,7 @@ from os.path import dirname, islink, realpath from optparse import OptionParser + def ignore_prefixes(module): """ Return a list of prefixes to ignore in the coverage report if we want to completely skip `module`. @@ -16,16 +17,17 @@ # distributions, such a Ubuntu, really like to build link farm in # /usr/lib in order to save a few bytes on the disk. dirnames = [dirname(module.__file__)] - + pymod = module.__file__.rstrip("c") if islink(pymod): dirnames.append(dirname(realpath(pymod))) return dirnames + def parse_opts(): parser = OptionParser(usage="%prog [OPTIONS]", description="run the distutils2 unittests") - + parser.add_option("-q", "--quiet", help="do not print verbose messages", action="store_true", default=False) parser.add_option("-c", "--coverage", action="store_true", default=False, @@ -36,10 +38,11 @@ default=False, help=("Show line numbers of statements in each module " "that weren't executed.")) - + opts, args = parser.parse_args() return opts, args + def coverage_report(opts): import coverage from distutils2.tests.support import unittest @@ -89,6 +92,7 @@ return ret + def run_tests(verbose): import distutils2.tests from distutils2.tests import run_unittest, reap_children, TestFailed @@ -108,12 +112,11 @@ finally: reap_children() + if __name__ == "__main__": try: from distutils2.tests.support import unittest except ImportError: sys.stderr.write('Error: You have to install unittest2') sys.exit(1) - sys.exit(test_main()) - diff --git a/src/runtests.py b/src/runtests.py --- a/src/runtests.py +++ b/src/runtests.py @@ -1,9 +1,12 @@ +#!/usr/bin/env python """Tests for distutils2. The tests for distutils2 are defined in the distutils2.tests package. """ + import sys + def test_main(): import distutils2.tests from distutils2.tests import run_unittest, reap_children, TestFailed @@ -23,12 +26,11 @@ finally: reap_children() + if __name__ == "__main__": try: from distutils2.tests.support import unittest except ImportError: sys.stderr.write('Error: You have to install unittest2') sys.exit(1) - sys.exit(test_main()) - -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:45 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:45 +0200 Subject: [Python-checkins] distutils2: Update developer notes Message-ID: tarek.ziade pushed 2c1b8e12d65b to distutils2: http://hg.python.org/distutils2/rev/2c1b8e12d65b changeset: 401:2c1b8e12d65b user: ?ric Araujo date: Sat Jul 31 12:57:05 2010 +0200 summary: Update developer notes files: src/DEVNOTES.txt diff --git a/src/DEVNOTES.txt b/src/DEVNOTES.txt --- a/src/DEVNOTES.txt +++ b/src/DEVNOTES.txt @@ -1,10 +1,20 @@ Notes for developers ==================== -- Distutils2 runs from 2.4 to 3.2 (3.x not implemented yet), so - make sure you don't use a syntax that doesn't work under +- Distutils2 runs on Python from 2.4 to 3.2 (3.x not implemented yet), + so make sure you don't use a syntax that doesn't work under one of these Python versions. - Always run tests.sh before you push a change. This implies - that you have all Python versions installed from 2.4 to 2.6. + that you have all Python versions installed from 2.4 to 2.7. +- With Python 2.4, if you want to run tests with runtests.py, or run + ust one test directly, be sure to run python2.4 setup.py build_ext + first, else tests won't find _hashlib or _md5. When using tests.sh, + build_ext is automatically done. + +- Renaming to do: + + - DistributionMetadata > Metadata or ReleaseMetadata + - pkgutil > pkgutil.__init__ + new pkgutil.database (or better name) + - pypi > index -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:45 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:45 +0200 Subject: [Python-checkins] distutils2: Change an option name to comply with the style of the other ones Message-ID: tarek.ziade pushed c63cf9c3a2ce to distutils2: http://hg.python.org/distutils2/rev/c63cf9c3a2ce changeset: 399:c63cf9c3a2ce user: ?ric Araujo date: Tue Jul 13 16:02:06 2010 +0200 summary: Change an option name to comply with the style of the other ones files: src/distutils2/command/sdist.py, src/distutils2/tests/test_sdist.py diff --git a/src/distutils2/command/sdist.py b/src/distutils2/command/sdist.py --- a/src/distutils2/command/sdist.py +++ b/src/distutils2/command/sdist.py @@ -70,7 +70,7 @@ ('dist-dir=', 'd', "directory to put the source distribution archive(s) in " "[default: dist]"), - ('medata-check', None, + ('check-metadata', None, "Ensure that all required elements of meta-data " "are supplied. Warn if any missing. [default]"), ('owner=', 'u', @@ -80,7 +80,7 @@ ] boolean_options = ['use-defaults', 'prune', - 'manifest-only', 'keep-temp', 'metadata-check'] + 'manifest-only', 'keep-temp', 'check-metadata'] help_options = [ ('help-formats', None, diff --git a/src/distutils2/tests/test_sdist.py b/src/distutils2/tests/test_sdist.py --- a/src/distutils2/tests/test_sdist.py +++ b/src/distutils2/tests/test_sdist.py @@ -241,7 +241,7 @@ @unittest.skipUnless(zlib, "requires zlib") def test_metadata_check_option(self): - # testing the `medata-check` option + # testing the `check-metadata` option dist, cmd = self.get_cmd(metadata={}) # this should raise some warnings ! -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:45 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:45 +0200 Subject: [Python-checkins] distutils2: Test env marker version without quotes Message-ID: tarek.ziade pushed 2278dcb5163d to distutils2: http://hg.python.org/distutils2/rev/2278dcb5163d changeset: 402:2278dcb5163d user: ?ric Araujo date: Sat Jul 31 13:06:33 2010 +0200 summary: Test env marker version without quotes files: src/distutils2/tests/test_metadata.py diff --git a/src/distutils2/tests/test_metadata.py b/src/distutils2/tests/test_metadata.py --- a/src/distutils2/tests/test_metadata.py +++ b/src/distutils2/tests/test_metadata.py @@ -30,7 +30,7 @@ # stuff that need to raise a syntax error ops = ('os.name == os.name', 'os.name == 2', "'2' == '2'", - 'okpjonon', '', 'os.name ==') + 'okpjonon', '', 'os.name ==', 'python_version == 2.4') for op in ops: self.assertRaises(SyntaxError, _interpret, op) -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:45 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:45 +0200 Subject: [Python-checkins] distutils2: Slight doc cleanup Message-ID: tarek.ziade pushed 4c44cb0697e2 to distutils2: http://hg.python.org/distutils2/rev/4c44cb0697e2 changeset: 403:4c44cb0697e2 user: ?ric Araujo date: Sat Jul 31 13:09:56 2010 +0200 summary: Slight doc cleanup files: src/distutils2/tests/test_pypi_versions.py diff --git a/src/distutils2/tests/test_pypi_versions.py b/src/distutils2/tests/test_pypi_versions.py --- a/src/distutils2/tests/test_pypi_versions.py +++ b/src/distutils2/tests/test_pypi_versions.py @@ -1,43 +1,39 @@ -# -## test_pypi_versions.py -## -## A very simple test to see what percentage of the current pypi packages -## have versions that can be converted automatically by distutils' new -## suggest_normalized_version() into PEP-386 compatible versions. -## -## Requires : Python 2.5+ -## -## Written by: ssteinerX at gmail.com -# +"""PEP 386 compatibility test with current distributions on PyPI. + +A very simple test to see what percentage of the current PyPI packages +have versions that can be converted automatically by distutils2's new +suggest_normalized_version into PEP 386-compatible versions. +""" + +# XXX This file does not actually run tests, move it to a script + +# Written by ssteinerX at gmail.com + +import os +import xmlrpclib try: import cPickle as pickle -except: +except ImportError: import pickle -import xmlrpclib -import os.path - from distutils2.version import suggest_normalized_version from distutils2.tests import run_unittest from distutils2.tests.support import unittest def test_pypi(): - # - ## To re-run from scratch, just delete these two .pkl files - # + # FIXME need a better way to do that + # To re-run from scratch, just delete these two .pkl files INDEX_PICKLE_FILE = 'pypi-index.pkl' VERSION_PICKLE_FILE = 'pypi-version.pkl' package_info = version_info = [] - # - ## if there's a saved version of the package list - ## restore it - ## else: - ## pull the list down from pypi - ## save a pickled version of it - # + # if there's a saved version of the package list + # restore it + # else: + # pull the list down from pypi + # save a pickled version of it if os.path.exists(INDEX_PICKLE_FILE): print "Loading saved pypi data..." f = open(INDEX_PICKLE_FILE, 'rb') @@ -57,13 +53,11 @@ finally: f.close() - # - ## If there's a saved list of the versions from the packages - ## restore it - ## else - ## extract versions from the package list - ## save a pickled version of it - # + # If there's a saved list of the versions from the packages + # restore it + # else + # extract versions from the package list + # save a pickled version of it versions = [] if os.path.exists(VERSION_PICKLE_FILE): print "Loading saved version info..." -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:45 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:45 +0200 Subject: [Python-checkins] distutils2: Improve tests.support docstrings and comments + minor code touchups Message-ID: tarek.ziade pushed bca32362740f to distutils2: http://hg.python.org/distutils2/rev/bca32362740f changeset: 404:bca32362740f user: ?ric Araujo date: Sat Jul 31 13:53:55 2010 +0200 summary: Improve tests.support docstrings and comments + minor code touchups files: src/distutils2/tests/support.py diff --git a/src/distutils2/tests/support.py b/src/distutils2/tests/support.py --- a/src/distutils2/tests/support.py +++ b/src/distutils2/tests/support.py @@ -3,6 +3,27 @@ Always import unittest from this module, it will be the right version (standard library unittest for 2.7 and higher, third-party unittest2 release for older versions). + +Three helper classes are provided: LoggingSilencer, TempdirManager and +EnvironGuard. They are written to be used as mixins, e.g. :: + + from distutils2.tests.support import unittest + from distutils2.tests.support import LoggingSilencer + + class SomeTestCase(LoggingSilencer, unittest.TestCase): + +If you need to define a setUp method on your test class, you have to +call the mixin class' setUp method or it won't work (same thing for +tearDown): + + def setUp(self): + super(self.__class__, self).setUp() + ... # other setup code + +Read each class' docstring to see their purpose and usage. + +Also provided is a DummyCommand class, useful to mock commands in the +tests of another command that needs them (see docstring). """ import os @@ -10,7 +31,6 @@ import shutil import tempfile from copy import deepcopy -import warnings from distutils2 import log from distutils2.log import DEBUG, INFO, WARN, ERROR, FATAL @@ -22,14 +42,25 @@ # external release of same package for older versions import unittest2 as unittest +__all__ = ['LoggingSilencer', 'TempdirManager', 'EnvironGuard', + 'DummyCommand', 'unittest'] + + class LoggingSilencer(object): + """TestCase-compatible mixin to catch logging calls. + + Every log message that goes through distutils2.log will get appended to + self.logs instead of being printed. You can check that your code logs + warnings and errors as documented by inspecting that list; helper methods + get_logs and clear_logs are also provided. + """ def setUp(self): super(LoggingSilencer, self).setUp() - self.threshold = log.set_threshold(log.FATAL) + self.threshold = log.set_threshold(FATAL) # catching warnings - # when log will be replaced by logging - # we won't need such monkey-patch anymore + # when log is replaced by logging we won't need + # such monkey-patching anymore self._old_log = log.Log._log log.Log._log = self._log self.logs = [] @@ -45,6 +76,10 @@ self.logs.append((level, msg, args)) def get_logs(self, *levels): + """Return a list of caught messages with level in `levels`. + + Example: self.get_logs(log.WARN, log.DEBUG) -> list + """ def _format(msg, args): if len(args) == 0: return msg @@ -53,13 +88,12 @@ in self.logs if level in levels] def clear_logs(self): - self.logs = [] + """Empty the internal list of caught messages.""" + del self.logs[:] + class TempdirManager(object): - """Mix-in class that handles temporary directories for test cases. - - This is intended to be used with unittest.TestCase. - """ + """TestCase-compatible mixin to handle temporary directories.""" def setUp(self): super(TempdirManager, self).setUp() @@ -82,19 +116,19 @@ return tempfile_ def mkdtemp(self): - """Create a temporary directory that will be cleaned up. + """Create a temporary directory that will be removed on exit. - Returns the path of the directory. + Return the path of the directory. """ d = tempfile.mkdtemp() self.tempdirs.append(d) return d def write_file(self, path, content='xxx'): - """Writes a file in the given path. + """Write a file at the given path. - - path can be a string or a sequence. + path can be a string, a tuple or a list; if it's a tuple or list, + os.path.join will be used to produce a path. """ if isinstance(path, (list, tuple)): path = os.path.join(*path) @@ -105,41 +139,50 @@ f.close() def create_dist(self, pkg_name='foo', **kw): - """Will generate a test environment. + """Create a stub distribution object and files. - This function creates: - - a Distribution instance using keywords - - a temporary directory with a package structure + This function creates a Distribution instance (use keyword arguments + to customize it) and a temporary directory with a project structure + (currently an empty directory). - It returns the package directory and the distribution - instance. + It returns the path to the directory and the Distribution instance. + You can use TempdirManager.write_file to write any file in that + directory, e.g. setup scripts or Python modules. """ + # Late import so that third parties can import support without + # loading a ton of distutils2 modules in memory. from distutils2.dist import Distribution tmp_dir = self.mkdtemp() pkg_dir = os.path.join(tmp_dir, pkg_name) os.mkdir(pkg_dir) dist = Distribution(attrs=kw) - return pkg_dir, dist -class DummyCommand: - """Class to store options for retrieval via set_undefined_options().""" + +class DummyCommand(object): + """Class to store options for retrieval via set_undefined_options(). + + Useful for mocking one dependency command in the tests for another + command, see e.g. the dummy build command in test_build_scripts. + """ def __init__(self, **kwargs): - for kw, val in kwargs.items(): + for kw, val in kwargs.iteritems(): setattr(self, kw, val) def ensure_finalized(self): pass + class EnvironGuard(object): + """TestCase-compatible mixin to save and restore the environment.""" def setUp(self): super(EnvironGuard, self).setUp() self.old_environ = deepcopy(os.environ) def tearDown(self): - for key, value in self.old_environ.items(): + for key, value in self.old_environ.iteritems(): if os.environ.get(key) != value: os.environ[key] = value -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:45 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:45 +0200 Subject: [Python-checkins] distutils2: Various fixes. Message-ID: tarek.ziade pushed 6309c707cbb3 to distutils2: http://hg.python.org/distutils2/rev/6309c707cbb3 changeset: 408:6309c707cbb3 user: ?ric Araujo date: Sat Jul 31 14:13:31 2010 +0200 summary: Various fixes. files: .hgignore, src/setup.cfg, src/tests.sh diff --git a/.hgignore b/.hgignore --- a/.hgignore +++ b/.hgignore @@ -1,9 +1,10 @@ syntax: glob *.py[co] __pycache__/ +*.so configure.cache build/ MANIFEST dist/ *.swp -.coverage \ No newline at end of file +.coverage diff --git a/src/setup.cfg b/src/setup.cfg new file mode 100644 --- /dev/null +++ b/src/setup.cfg @@ -0,0 +1,3 @@ +[build_ext] +# needed so that tests work without mucking with sys.path +inplace = 1 diff --git a/src/tests.sh b/src/tests.sh --- a/src/tests.sh +++ b/src/tests.sh @@ -1,20 +1,18 @@ #!/bin/sh echo -n "Running tests for Python 2.4... " -rm -rf *.so -python2.4 setup.py build_ext -i -q 2> /dev/null > /dev/null +rm -f _hashlib.so +python2.4 setup.py build_ext -q 2> /dev/null > /dev/null python2.4 -Wd runtests.py -q 2> /dev/null -rm -rf *.so if [ $? -ne 0 ];then echo "Failed" + rm -f _hashlib.so exit 1 else echo "Success" fi echo -n "Running tests for Python 2.5... " -python2.5 setup.py build_ext -i -q 2> /dev/null > /dev/null python2.5 -Wd runtests.py -q 2> /dev/null -rm -rf *.so if [ $? -ne 0 ];then echo "Failed" exit 1 -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:45 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:45 +0200 Subject: [Python-checkins] distutils2: Make the PyPI mock server always power off. Message-ID: tarek.ziade pushed 01cb51430c05 to distutils2: http://hg.python.org/distutils2/rev/01cb51430c05 changeset: 411:01cb51430c05 user: ?ric Araujo date: Sat Jul 31 14:57:53 2010 +0200 summary: Make the PyPI mock server always power off. files: src/distutils2/tests/pypi_server.py diff --git a/src/distutils2/tests/pypi_server.py b/src/distutils2/tests/pypi_server.py --- a/src/distutils2/tests/pypi_server.py +++ b/src/distutils2/tests/pypi_server.py @@ -7,8 +7,6 @@ import Queue import threading -import time -import urllib2 from BaseHTTPServer import HTTPServer from SimpleHTTPServer import SimpleHTTPRequestHandler import os.path @@ -41,8 +39,8 @@ self.pypi.start() def tearDown(self): + self.pypi.stop() super(PyPIServerTestCase, self).tearDown() - self.pypi.stop() class PyPIServer(threading.Thread): """PyPI Mocked server. -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:45 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:45 +0200 Subject: [Python-checkins] distutils2: Fix typos, spell ?metadata? consistently, update one docstring Message-ID: tarek.ziade pushed f425bb4064f3 to distutils2: http://hg.python.org/distutils2/rev/f425bb4064f3 changeset: 409:f425bb4064f3 user: ?ric Araujo date: Sat Jul 31 15:32:43 2010 +0200 summary: Fix typos, spell ?metadata? consistently, update one docstring files: docs/design/pep-0376.txt, src/distutils2/command/check.py, src/distutils2/command/register.py, src/distutils2/command/sdist.py, src/distutils2/command/upload.py, src/distutils2/core.py, src/distutils2/dist.py, src/distutils2/metadata.py, src/distutils2/tests/test_check.py, src/distutils2/tests/test_register.py diff --git a/docs/design/pep-0376.txt b/docs/design/pep-0376.txt --- a/docs/design/pep-0376.txt +++ b/docs/design/pep-0376.txt @@ -17,7 +17,7 @@ This PEP proposes various enhancements for Distutils: - A new format for the .egg-info structure. -- Some APIs to read the meta-data of a distribution. +- Some APIs to read the metadata of a distribution. - A replacement PEP 262. - An uninstall feature. diff --git a/src/distutils2/command/check.py b/src/distutils2/command/check.py --- a/src/distutils2/command/check.py +++ b/src/distutils2/command/check.py @@ -8,13 +8,13 @@ from distutils2.errors import DistutilsSetupError class check(Command): - """This command checks the meta-data of the package. + """This command checks the metadata of the package. """ description = ("perform some checks on the package") - user_options = [('metadata', 'm', 'Verify meta-data'), + user_options = [('metadata', 'm', 'Verify metadata'), ('restructuredtext', 'r', - ('Checks if long string meta-data syntax ' - 'are reStructuredText-compliant')), + ('Checks if long string metadata syntax ' + 'is reStructuredText-compliant')), ('strict', 's', 'Will exit with an error if a check fails')] @@ -53,7 +53,7 @@ raise DistutilsSetupError(msg) def check_metadata(self): - """Ensures that all required elements of meta-data are supplied. + """Ensures that all required elements of metadata are supplied. name, version, URL, (author and author_email) or (maintainer and maintainer_email)). @@ -62,7 +62,7 @@ """ missing, __ = self.distribution.metadata.check() if missing != []: - self.warn("missing required meta-data: %s" % ', '.join(missing)) + self.warn("missing required metadata: %s" % ', '.join(missing)) def check_restructuredtext(self): """Checks if the long string fields are reST-compliant.""" diff --git a/src/distutils2/command/register.py b/src/distutils2/command/register.py --- a/src/distutils2/command/register.py +++ b/src/distutils2/command/register.py @@ -29,7 +29,7 @@ ('list-classifiers', None, 'list the valid Trove classifiers'), ('strict', None , - 'Will stop the registering if the meta-data are not fully compliant') + 'Will stop the registering if the metadata is not fully compliant') ] boolean_options = ['show-response', 'verify', 'list-classifiers', diff --git a/src/distutils2/command/sdist.py b/src/distutils2/command/sdist.py --- a/src/distutils2/command/sdist.py +++ b/src/distutils2/command/sdist.py @@ -71,7 +71,7 @@ "directory to put the source distribution archive(s) in " "[default: dist]"), ('check-metadata', None, - "Ensure that all required elements of meta-data " + "Ensure that all required elements of metadata " "are supplied. Warn if any missing. [default]"), ('owner=', 'u', "Owner name used when creating a tar file [default: current user]"), @@ -362,7 +362,7 @@ 'self.keep_temp' is true). The list of archive files created is stored so it can be retrieved later by 'get_archive_files()'. """ - # Don't warn about missing meta-data here -- should be (and is!) + # Don't warn about missing metadata here -- should be (and is!) # done elsewhere. base_dir = self.distribution.get_fullname() base_name = os.path.join(self.dist_dir, base_dir) diff --git a/src/distutils2/command/upload.py b/src/distutils2/command/upload.py --- a/src/distutils2/command/upload.py +++ b/src/distutils2/command/upload.py @@ -94,7 +94,7 @@ spawn(gpg_args, dry_run=self.dry_run) - # Fill in the data - send all the meta-data in case we need to + # Fill in the data - send all the metadata in case we need to # register a new release content = open(filename,'rb').read() diff --git a/src/distutils2/core.py b/src/distutils2/core.py --- a/src/distutils2/core.py +++ b/src/distutils2/core.py @@ -155,7 +155,7 @@ def run_setup(script_name, script_args=None, stop_after="run"): """Run a setup script in a somewhat controlled environment, and return the Distribution instance that drives things. This is useful - if you need to find out the distribution meta-data (passed as + if you need to find out the distribution metadata (passed as keyword args from 'script' to 'setup()', or the contents of the config files or command-line. diff --git a/src/distutils2/dist.py b/src/distutils2/dist.py --- a/src/distutils2/dist.py +++ b/src/distutils2/dist.py @@ -145,7 +145,7 @@ for attr in self.display_option_names: setattr(self, attr, 0) - # Store the distribution meta-data (name, version, author, and so + # Store the distribution metadata (name, version, author, and so # forth) in a separate object -- we're getting to have enough # information here (and enough command-line options) that it's # worth it. diff --git a/src/distutils2/metadata.py b/src/distutils2/metadata.py --- a/src/distutils2/metadata.py +++ b/src/distutils2/metadata.py @@ -182,7 +182,7 @@ class DistributionMetadata(object): - """Distribution meta-data class (1.0 or 1.2). + """Distribution metadata class (versions 1.0, 1.1 and 1.2 supported). """ def __init__(self, path=None, platform_dependent=False, execution_context=None, fileobj=None): @@ -399,7 +399,7 @@ return value def check(self): - """Checks if the metadata are compliant.""" + """Checks if the metadata is compliant.""" # XXX should check the versions (if the file was loaded) missing = [] for attr in ('Name', 'Version', 'Home-page'): diff --git a/src/distutils2/tests/test_check.py b/src/distutils2/tests/test_check.py --- a/src/distutils2/tests/test_check.py +++ b/src/distutils2/tests/test_check.py @@ -43,7 +43,7 @@ # get an error if there are missing metadata self.assertRaises(DistutilsSetupError, self._run, {}, **{'strict': 1}) - # and of course, no error when all metadata are present + # and of course, no error when all metadata fields are present cmd = self._run(metadata, strict=1) self.assertEqual(len(cmd._warnings), 0) diff --git a/src/distutils2/tests/test_register.py b/src/distutils2/tests/test_register.py --- a/src/distutils2/tests/test_register.py +++ b/src/distutils2/tests/test_register.py @@ -210,7 +210,7 @@ cmd.strict = 1 self.assertRaises(DistutilsSetupError, cmd.run) - # metadata are OK but long_description is broken + # metadata is OK but long_description is broken metadata = {'home_page': 'xxx', 'author': 'xxx', 'author_email': u'??x??x??', 'name': 'xxx', 'version': 'xxx', -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:45 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:45 +0200 Subject: [Python-checkins] distutils2: Minor style and wording fixes Message-ID: tarek.ziade pushed 8152fcbe4279 to distutils2: http://hg.python.org/distutils2/rev/8152fcbe4279 changeset: 413:8152fcbe4279 user: ?ric Araujo date: Sat Jul 31 15:26:55 2010 +0200 summary: Minor style and wording fixes files: src/distutils2/command/register.py, src/distutils2/command/upload.py, src/distutils2/command/upload_docs.py diff --git a/src/distutils2/command/register.py b/src/distutils2/command/register.py --- a/src/distutils2/command/register.py +++ b/src/distutils2/command/register.py @@ -21,15 +21,16 @@ class register(Command): - description = ("register the distribution with the Python package index") - user_options = [('repository=', 'r', - "url of repository [default: %s]" % DEFAULT_REPOSITORY), + description = "register the distribution with the Python package index" + user_options = [ + ('repository=', 'r', + "repository URL [default: %s]" % DEFAULT_REPOSITORY), ('show-response', None, - 'display full response text from server'), + "display full response text from server"), ('list-classifiers', None, - 'list the valid Trove classifiers'), + "list valid Trove classifiers"), ('strict', None , - 'Will stop the registering if the metadata is not fully compliant') + "stop the registration if the metadata is not fully compliant") ] boolean_options = ['show-response', 'verify', 'list-classifiers', diff --git a/src/distutils2/command/upload.py b/src/distutils2/command/upload.py --- a/src/distutils2/command/upload.py +++ b/src/distutils2/command/upload.py @@ -24,16 +24,17 @@ class upload(Command): - description = "upload binary package to PyPI" + description = "upload distribution to PyPI" - user_options = [('repository=', 'r', - "url of repository [default: %s]" % \ - DEFAULT_REPOSITORY), + user_options = [ + ('repository=', 'r', + "repository URL [default: %s]" % DEFAULT_REPOSITORY), ('show-response', None, - 'display full response text from server'), + "display full response text from server"), ('sign', 's', - 'sign files to upload using gpg'), - ('identity=', 'i', 'GPG identity used to sign files'), + "sign files to upload using gpg"), + ('identity=', 'i', + "GPG identity used to sign files"), ] boolean_options = ['show-response', 'sign'] diff --git a/src/distutils2/command/upload_docs.py b/src/distutils2/command/upload_docs.py --- a/src/distutils2/command/upload_docs.py +++ b/src/distutils2/command/upload_docs.py @@ -52,9 +52,12 @@ class upload_docs(Command): user_options = [ - ('repository=', 'r', "url of repository [default: %s]" % DEFAULT_REPOSITORY), - ('show-response', None, 'display full response text from server'), - ('upload-dir=', None, 'directory to upload'), + ('repository=', 'r', + "repository URL [default: %s]" % DEFAULT_REPOSITORY), + ('show-response', None, + "display full response text from server"), + ('upload-dir=', None, + "directory to upload"), ] def initialize_options(self): -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:45 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:45 +0200 Subject: [Python-checkins] distutils2: Run pyflakes and pep8 on test_pypi_dist Message-ID: tarek.ziade pushed 0b78541305d0 to distutils2: http://hg.python.org/distutils2/rev/0b78541305d0 changeset: 417:0b78541305d0 user: ?ric Araujo date: Sat Jul 31 17:19:27 2010 +0200 summary: Run pyflakes and pep8 on test_pypi_dist files: src/distutils2/tests/test_pypi_dist.py diff --git a/src/distutils2/tests/test_pypi_dist.py b/src/distutils2/tests/test_pypi_dist.py --- a/src/distutils2/tests/test_pypi_dist.py +++ b/src/distutils2/tests/test_pypi_dist.py @@ -1,9 +1,5 @@ """Tests for the distutils2.pypi.dist module.""" -import os -import shutil -import tempfile - from distutils2.tests.pypi_server import use_pypi_server from distutils2.tests import run_unittest from distutils2.tests.support import unittest, TempdirManager @@ -18,7 +14,7 @@ unittest.TestCase): """Tests the pypi.dist.PyPIDistribution class""" - def test_instanciation(self): + def test_instantiation(self): # Test the Distribution class provides us the good attributes when # given on construction dist = Dist("FooBar", "1.1") @@ -63,7 +59,7 @@ self.assertEqual(value[val], mylist[val]) else: if attribute == "version": - self.assertEqual("%s" % getattr(dist, "version"), value) + self.assertEqual(str(getattr(dist, "version")), value) else: self.assertEqual(getattr(dist, attribute), value) @@ -120,13 +116,13 @@ url = "%s/simple/foobar/foobar-0.1.tar.gz" % server.full_address # check md5 if given - dist = Dist("FooBar", "0.1", url=url, - url_hashname="md5", url_hashval="d41d8cd98f00b204e9800998ecf8427e") add_to_tmpdirs(dist.download()) + dist = Dist("FooBar", "0.1", url=url, url_hashname="md5", + url_hashval="d41d8cd98f00b204e9800998ecf8427e") # a wrong md5 fails dist2 = Dist("FooBar", "0.1", url=url, - url_hashname="md5", url_hashval="wrongmd5") + url_hashname="md5", url_hashval="wrongmd5") self.assertRaises(HashDoesNotMatch, dist2.download) add_to_tmpdirs(dist2.downloaded_location) -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:45 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:45 +0200 Subject: [Python-checkins] distutils2: Make test skip output more consistent Message-ID: tarek.ziade pushed 2d944d2852fa to distutils2: http://hg.python.org/distutils2/rev/2d944d2852fa changeset: 410:2d944d2852fa user: ?ric Araujo date: Sat Jul 31 14:49:22 2010 +0200 summary: Make test skip output more consistent files: src/distutils2/tests/test_Mixin2to3.py, src/distutils2/tests/test_bdist_msi.py, src/distutils2/tests/test_build_ext.py, src/distutils2/tests/test_build_py.py, src/distutils2/tests/test_converter.py, src/distutils2/tests/test_install_lib.py, src/distutils2/tests/test_msvc9compiler.py, src/distutils2/tests/test_sdist.py, src/distutils2/tests/test_util.py diff --git a/src/distutils2/tests/test_Mixin2to3.py b/src/distutils2/tests/test_Mixin2to3.py --- a/src/distutils2/tests/test_Mixin2to3.py +++ b/src/distutils2/tests/test_Mixin2to3.py @@ -10,7 +10,7 @@ class Mixin2to3TestCase(support.TempdirManager, unittest.TestCase): - @unittest.skipUnless(sys.version > '2.6', 'Need >= 2.6') + @unittest.skipIf(sys.version < '2.6', 'requires Python 2.6 or higher') def test_convert_code_only(self): # used to check if code gets converted properly. code_content = "print 'test'\n" @@ -27,7 +27,7 @@ self.assertEquals(new_code_content, converted_code_content) - @unittest.skipUnless(sys.version > '2.6', 'Need >= 2.6') + @unittest.skipIf(sys.version < '2.6', 'requires Python 2.6 or higher') def test_doctests_only(self): # used to check if doctests gets converted properly. doctest_content = '"""\n>>> print test\ntest\n"""\nprint test\n\n' diff --git a/src/distutils2/tests/test_bdist_msi.py b/src/distutils2/tests/test_bdist_msi.py --- a/src/distutils2/tests/test_bdist_msi.py +++ b/src/distutils2/tests/test_bdist_msi.py @@ -10,8 +10,8 @@ support.LoggingSilencer, unittest.TestCase): - @unittest.skipUnless(sys.platform=="win32", "These tests are only for win32") - def test_minial(self): + @unittest.skipUnless(sys.platform == "win32", "runs only on win32") + def test_minimal(self): # minimal test XXX need more tests from distutils2.command.bdist_msi import bdist_msi pkg_pth, dist = self.create_dist() diff --git a/src/distutils2/tests/test_build_ext.py b/src/distutils2/tests/test_build_ext.py --- a/src/distutils2/tests/test_build_ext.py +++ b/src/distutils2/tests/test_build_ext.py @@ -45,7 +45,7 @@ build_ext.USER_BASE = site.USER_BASE # XXX only works with 2.6 > -- dunno why yet - @unittest.skipUnless(sys.version_info >= (2, 6,), 'works for >= 2.6') + @unittest.skipIf(sys.version < '2.6', 'requires Python 2.6 or higher') def test_build_ext(self): global ALREADY_TESTED xx_c = os.path.join(self.tmp_dir, 'xxmodule.c') diff --git a/src/distutils2/tests/test_build_py.py b/src/distutils2/tests/test_build_py.py --- a/src/distutils2/tests/test_build_py.py +++ b/src/distutils2/tests/test_build_py.py @@ -95,7 +95,7 @@ sys.stdout = old_stdout @unittest.skipUnless(hasattr(sys, 'dont_write_bytecode'), - 'dont_write_bytecode support') + 'sys.dont_write_bytecode not supported') def test_dont_write_bytecode(self): # makes sure byte_compile is not used pkg_dir, dist = self.create_dist() diff --git a/src/distutils2/tests/test_converter.py b/src/distutils2/tests/test_converter.py --- a/src/distutils2/tests/test_converter.py +++ b/src/distutils2/tests/test_converter.py @@ -17,7 +17,7 @@ class ConverterTestCase(unittest.TestCase): - @unittest.skipUnless(not sys.version < '2.6', 'Needs Python >=2.6') + @unittest.skipIf(sys.version < '2.6', 'requires Python 2.6 or higher') def test_conversions(self): # for all XX_before in the conversions/ dir # we run the refactoring tool diff --git a/src/distutils2/tests/test_install_lib.py b/src/distutils2/tests/test_install_lib.py --- a/src/distutils2/tests/test_install_lib.py +++ b/src/distutils2/tests/test_install_lib.py @@ -82,7 +82,8 @@ # get_input should return 2 elements self.assertEqual(len(cmd.get_inputs()), 2) - @unittest.skipUnless(bytecode_support, 'sys.dont_write_bytecode not supported') + @unittest.skipUnless(bytecode_support, + 'sys.dont_write_bytecode not supported') def test_dont_write_bytecode(self): # makes sure byte_compile is not used pkg_dir, dist = self.create_dist() diff --git a/src/distutils2/tests/test_msvc9compiler.py b/src/distutils2/tests/test_msvc9compiler.py --- a/src/distutils2/tests/test_msvc9compiler.py +++ b/src/distutils2/tests/test_msvc9compiler.py @@ -64,7 +64,7 @@ class msvc9compilerTestCase(support.TempdirManager, unittest.TestCase): - @unittest.skipUnless(sys.platform=="win32", "These tests are only for win32") + @unittest.skipUnless(sys.platform == "win32", "runs only on win32") def test_no_compiler(self): # makes sure query_vcvarsall throws # a DistutilsPlatformError if the compiler @@ -86,7 +86,7 @@ finally: msvc9compiler.find_vcvarsall = old_find_vcvarsall - @unittest.skipUnless(sys.platform=="win32", "These tests are only for win32") + @unittest.skipUnless(sys.platform == "win32", "runs only on win32") def test_reg_class(self): from distutils2.msvccompiler import get_build_version if get_build_version() < 8.0: @@ -110,7 +110,7 @@ keys = Reg.read_keys(HKCU, r'Control Panel') self.assertTrue('Desktop' in keys) - @unittest.skipUnless(sys.platform=="win32", "These tests are only for win32") + @unittest.skipUnless(sys.platform == "win32", "runs only on win32") def test_remove_visual_c_ref(self): from distutils2.msvc9compiler import MSVCCompiler tempdir = self.mkdtemp() diff --git a/src/distutils2/tests/test_sdist.py b/src/distutils2/tests/test_sdist.py --- a/src/distutils2/tests/test_sdist.py +++ b/src/distutils2/tests/test_sdist.py @@ -295,7 +295,7 @@ self.assertRaises(DistutilsOptionError, cmd.finalize_options) @unittest.skipUnless(zlib, "requires zlib") - @unittest.skipUnless(UID_GID_SUPPORT, "Requires grp and pwd support") + @unittest.skipUnless(UID_GID_SUPPORT, "requires grp and pwd support") def test_make_distribution_owner_group(self): # check if tar and gzip are installed diff --git a/src/distutils2/tests/test_util.py b/src/distutils2/tests/test_util.py --- a/src/distutils2/tests/test_util.py +++ b/src/distutils2/tests/test_util.py @@ -288,7 +288,7 @@ self.assertEqual(res[2], None) @unittest.skipUnless(hasattr(sys, 'dont_write_bytecode'), - 'no dont_write_bytecode support') + 'sys.dont_write_bytecode not supported') def test_dont_write_bytecode(self): # makes sure byte_compile raise a DistutilsError # if sys.dont_write_bytecode is True @@ -343,7 +343,7 @@ res = find_packages([root], ['pkg1.pkg2']) self.assertEqual(set(res), set(['pkg1', 'pkg5', 'pkg1.pkg3', 'pkg1.pkg3.pkg6'])) - @unittest.skipUnless(sys.version > '2.6', 'Need Python 2.6 or more') + @unittest.skipIf(sys.version < '2.6', 'requires Python 2.6 or higher') def test_run_2to3_on_code(self): content = "print 'test'" converted_content = "print('test')" @@ -358,7 +358,7 @@ file_handle.close() self.assertEquals(new_content, converted_content) - @unittest.skipUnless(sys.version > '2.6', 'Need Python 2.6 or more') + @unittest.skipIf(sys.version < '2.6', 'requires Python 2.6 or higher') def test_run_2to3_on_doctests(self): # to check if text files containing doctests only get converted. content = ">>> print 'test'\ntest\n" @@ -385,7 +385,7 @@ @unittest.skipUnless(os.name in ('nt', 'posix'), - 'Runs only under posix or nt') + 'runs only under posix or nt') def test_spawn(self): tmpdir = self.mkdtemp() -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:45 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:45 +0200 Subject: [Python-checkins] distutils2: Touch up some docstrings and comments Message-ID: tarek.ziade pushed fe49dd1e9436 to distutils2: http://hg.python.org/distutils2/rev/fe49dd1e9436 changeset: 414:fe49dd1e9436 user: ?ric Araujo date: Sat Jul 31 15:33:24 2010 +0200 summary: Touch up some docstrings and comments files: src/distutils2/metadata.py diff --git a/src/distutils2/metadata.py b/src/distutils2/metadata.py --- a/src/distutils2/metadata.py +++ b/src/distutils2/metadata.py @@ -1,7 +1,6 @@ -""" -Implementation of the Metadata for Python packages +"""Implementation of the Metadata for Python packages PEPs. -Supports all Metadata formats (1.0, 1.1, 1.2). +Supports all metadata formats (1.0, 1.1, 1.2). """ import re @@ -95,7 +94,7 @@ raise MetadataUnrecognizedVersionError(version) def _best_version(fields): - """Will detect the best version depending on the fields used.""" + """Detect the best version depending on the fields used.""" def _has_marker(keys, markers): for marker in markers: if marker in keys: @@ -182,7 +181,9 @@ class DistributionMetadata(object): - """Distribution metadata class (versions 1.0, 1.1 and 1.2 supported). + """The metadata of a release. + + Supports versions 1.0, 1.1 and 1.2 (auto-detected). """ def __init__(self, path=None, platform_dependent=False, execution_context=None, fileobj=None): @@ -232,7 +233,7 @@ return 'UNKNOWN' def _check_rst_data(self, data): - """Returns warnings when the provided data doesn't compile.""" + """Return warnings when the provided data has syntax errors.""" source_path = StringIO() parser = Parser() settings = frontend.OptionParser().get_default_values() @@ -267,7 +268,7 @@ return _LINE_PREFIX.sub('\n', value) # - # Public APIs + # Public API # def get_fullname(self): return '%s-%s' % (self['Name'], self['Version']) @@ -280,7 +281,7 @@ self.read_file(open(filepath)) def read_file(self, fileob): - """Reads the metadata values from a file object.""" + """Read the metadata values from a file object.""" msg = message_from_file(fileob) self.version = msg['metadata-version'] @@ -298,8 +299,7 @@ self.set(field, value) def write(self, filepath): - """Write the metadata fields into path. - """ + """Write the metadata fields to filepath.""" pkg_info = open(filepath, 'w') try: self.write_file(pkg_info) @@ -307,8 +307,7 @@ pkg_info.close() def write_file(self, fileobject): - """Write the PKG-INFO format data to a file object. - """ + """Write the PKG-INFO format data to a file object.""" self._set_best_version() for field in _version2fieldlist(self.version): values = self.get(field) @@ -327,7 +326,7 @@ self._write_field(fileobject, field, value) def set(self, name, value): - """Controls then sets a metadata field""" + """Control then set a metadata field.""" name = self._convert_name(name) if (name in _ELEMENTSFIELD + ('Platform',) and @@ -364,7 +363,7 @@ self._set_best_version() def get(self, name): - """Gets a metadata field.""" + """Get a metadata field.""" name = self._convert_name(name) if name not in self._fields: return self._default_value(name) @@ -399,7 +398,7 @@ return value def check(self): - """Checks if the metadata is compliant.""" + """Check if the metadata is compliant.""" # XXX should check the versions (if the file was loaded) missing = [] for attr in ('Name', 'Version', 'Home-page'): @@ -625,7 +624,7 @@ return True def _interpret(marker, execution_context=None): - """Interprets a marker and return a result given the environment.""" + """Interpret a marker and return a result depending on environment.""" marker = marker.strip() operations = _CHAIN(execution_context) tokenize(StringIO(marker).readline, operations.eat) -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:45 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:45 +0200 Subject: [Python-checkins] distutils2: Allow running all tests via python somefile.py Message-ID: tarek.ziade pushed c97c7194fc01 to distutils2: http://hg.python.org/distutils2/rev/c97c7194fc01 changeset: 412:c97c7194fc01 user: ?ric Araujo date: Sat Jul 31 15:06:02 2010 +0200 summary: Allow running all tests via python somefile.py files: src/distutils2/_backport/tests/__init__.py, src/distutils2/tests/__init__.py, src/distutils2/tests/test_cmd.py diff --git a/src/distutils2/_backport/tests/__init__.py b/src/distutils2/_backport/tests/__init__.py --- a/src/distutils2/_backport/tests/__init__.py +++ b/src/distutils2/_backport/tests/__init__.py @@ -4,7 +4,7 @@ from distutils2.tests.support import unittest -here = os.path.dirname(__file__) +here = os.path.dirname(__file__) or '.' def test_suite(): suite = unittest.TestSuite() @@ -16,4 +16,5 @@ suite.addTest(module.test_suite()) return suite - +if __name__ == '__main__': + unittest.main(defaultTest='test_suite') diff --git a/src/distutils2/tests/__init__.py b/src/distutils2/tests/__init__.py --- a/src/distutils2/tests/__init__.py +++ b/src/distutils2/tests/__init__.py @@ -23,7 +23,7 @@ from test.test_support import TESTFN # use TESTFN from stdlib/test_support. -here = os.path.dirname(__file__) +here = os.path.dirname(__file__) or '.' verbose = 1 diff --git a/src/distutils2/tests/test_cmd.py b/src/distutils2/tests/test_cmd.py --- a/src/distutils2/tests/test_cmd.py +++ b/src/distutils2/tests/test_cmd.py @@ -98,7 +98,7 @@ def test_ensure_dirname(self): cmd = self.cmd - cmd.option1 = os.path.dirname(__file__) + cmd.option1 = os.path.dirname(__file__) or '.' cmd.ensure_dirname('option1') cmd.option2 = 'xxx' self.assertRaises(DistutilsOptionError, cmd.ensure_dirname, 'option2') -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:45 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:45 +0200 Subject: [Python-checkins] distutils2: Fix a bug in environment marker micro-language parser Message-ID: tarek.ziade pushed f8050faac794 to distutils2: http://hg.python.org/distutils2/rev/f8050faac794 changeset: 415:f8050faac794 user: ?ric Araujo date: Sat Jul 31 15:59:25 2010 +0200 summary: Fix a bug in environment marker micro-language parser files: src/distutils2/metadata.py, src/distutils2/tests/test_metadata.py diff --git a/src/distutils2/metadata.py b/src/distutils2/metadata.py --- a/src/distutils2/metadata.py +++ b/src/distutils2/metadata.py @@ -470,8 +470,8 @@ sys.version_info[1]), 'python_full_version': sys.version.split()[0], 'os.name': os.name, - 'platform.version': platform.version, - 'platform.machine': platform.machine} + 'platform.version': platform.version(), + 'platform.machine': platform.machine()} class _Operation(object): diff --git a/src/distutils2/tests/test_metadata.py b/src/distutils2/tests/test_metadata.py --- a/src/distutils2/tests/test_metadata.py +++ b/src/distutils2/tests/test_metadata.py @@ -1,6 +1,7 @@ """Tests for distutils.command.bdist.""" import os import sys +import platform from StringIO import StringIO from distutils2.metadata import (DistributionMetadata, _interpret, @@ -14,19 +15,23 @@ def test_interpret(self): - platform = sys.platform + sys_platform = sys.platform version = sys.version.split()[0] os_name = os.name + platform_version = platform.version() + platform_machine = platform.machine() - self.assertTrue(_interpret("sys.platform == '%s'" % platform)) + self.assertTrue(_interpret("sys.platform == '%s'" % sys_platform)) self.assertTrue(_interpret( - "sys.platform == '%s' or python_version == '2.4'" % platform)) + "sys.platform == '%s' or python_version == '2.4'" % sys_platform)) self.assertTrue(_interpret( "sys.platform == '%s' and python_full_version == '%s'" % - (platform, version))) - self.assertTrue(_interpret("'%s' == sys.platform" % platform)) - + (sys_platform, version))) + self.assertTrue(_interpret("'%s' == sys.platform" % sys_platform)) self.assertTrue(_interpret('os.name == "%s"' % os_name)) + self.assertTrue(_interpret( + 'platform.version == "%s" and platform.machine == "%s"' % + (platform_version, platform_machine))) # stuff that need to raise a syntax error ops = ('os.name == os.name', 'os.name == 2', "'2' == '2'", -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:45 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:45 +0200 Subject: [Python-checkins] distutils2: Remove obsolete references to Mac OS 9, unsupported since Python 2.4 Message-ID: tarek.ziade pushed a33cd046903d to distutils2: http://hg.python.org/distutils2/rev/a33cd046903d changeset: 420:a33cd046903d user: ?ric Araujo date: Sat Jul 31 18:11:30 2010 +0200 summary: Remove obsolete references to Mac OS 9, unsupported since Python 2.4 files: src/distutils2/_backport/tarfile.py, src/distutils2/util.py diff --git a/src/distutils2/_backport/tarfile.py b/src/distutils2/_backport/tarfile.py --- a/src/distutils2/_backport/tarfile.py +++ b/src/distutils2/_backport/tarfile.py @@ -56,13 +56,6 @@ if not hasattr(os, 'SEEK_SET'): os.SEEK_SET = 0 -if sys.platform == 'mac': - # This module needs work for MacOS9, especially in the area of pathname - # handling. In many places it is assumed a simple substitution of / by the - # local os.path.sep is good enough to convert pathnames, but this does not - # work with the mac rooted:path:name versus :nonrooted:path:name syntax - raise ImportError, "tarfile does not work for platform==mac" - try: import grp, pwd except ImportError: diff --git a/src/distutils2/util.py b/src/distutils2/util.py --- a/src/distutils2/util.py +++ b/src/distutils2/util.py @@ -117,15 +117,6 @@ path = path[1:] return os.path.join(new_root, path) - elif os.name == 'mac': - if not os.path.isabs(pathname): - return os.path.join(new_root, pathname) - else: - # Chop off volume name from start of path - elements = pathname.split(":", 1) - pathname = ":" + elements[1] - return os.path.join(new_root, pathname) - else: raise DistutilsPlatformError("nothing known about " "platform '%s'" % os.name) -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:45 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:45 +0200 Subject: [Python-checkins] distutils2: Code cleanup Message-ID: tarek.ziade pushed 93a29f88b85f to distutils2: http://hg.python.org/distutils2/rev/93a29f88b85f changeset: 416:93a29f88b85f user: ?ric Araujo date: Sat Jul 31 16:06:07 2010 +0200 summary: Code cleanup files: src/distutils2/core.py, src/distutils2/metadata.py, src/distutils2/tests/test_core.py, src/distutils2/tests/test_register.py diff --git a/src/distutils2/core.py b/src/distutils2/core.py --- a/src/distutils2/core.py +++ b/src/distutils2/core.py @@ -205,8 +205,6 @@ # Hmm, should we do something if exiting with a non-zero code # (ie. error)? pass - except: - raise if _setup_distribution is None: raise RuntimeError, \ diff --git a/src/distutils2/metadata.py b/src/distutils2/metadata.py --- a/src/distutils2/metadata.py +++ b/src/distutils2/metadata.py @@ -77,12 +77,11 @@ 'Obsoletes-Dist', 'Requires-External', 'Maintainer', 'Maintainer-email', 'Project-URL') -_ALL_FIELDS = [] +_ALL_FIELDS = set() +_ALL_FIELDS.update(_241_FIELDS) +_ALL_FIELDS.update(_314_FIELDS) +_ALL_FIELDS.update(_345_FIELDS) -for field in _241_FIELDS + _314_FIELDS + _345_FIELDS: - if field in _ALL_FIELDS: - continue - _ALL_FIELDS.append(field) def _version2fieldlist(version): if version == '1.0': @@ -223,12 +222,10 @@ if name in _ALL_FIELDS: return name name = name.replace('-', '_').lower() - if name in _ATTR2FIELD: - return _ATTR2FIELD[name] - return name + return _ATTR2FIELD.get(name, name) def _default_value(self, name): - if name in _LISTFIELDS + _ELEMENTSFIELD: + if name in _LISTFIELDS or name in _ELEMENTSFIELD: return [] return 'UNKNOWN' @@ -329,14 +326,14 @@ """Control then set a metadata field.""" name = self._convert_name(name) - if (name in _ELEMENTSFIELD + ('Platform',) and + if ((name in _ELEMENTSFIELD or name == 'Platform') and not isinstance(value, (list, tuple))): if isinstance(value, str): value = value.split(',') else: value = [] elif (name in _LISTFIELDS and - not isinstance(value, (list, tuple))): + not isinstance(value, (list, tuple))): if isinstance(value, str): value = [value] else: @@ -400,7 +397,7 @@ def check(self): """Check if the metadata is compliant.""" # XXX should check the versions (if the file was loaded) - missing = [] + missing, warnings = [], [] for attr in ('Name', 'Version', 'Home-page'): value = self[attr] if value == 'UNKNOWN': @@ -408,14 +405,11 @@ if _HAS_DOCUTILS: warnings = self._check_rst_data(self['Description']) - else: - warnings = [] # checking metadata 1.2 (XXX needs to check 1.1, 1.0) if self['Metadata-Version'] != '1.2': return missing, warnings - def is_valid_predicates(value): for v in value: if not is_valid_predicate(v.split(';')[0]): @@ -423,16 +417,15 @@ return True for fields, controller in ((_PREDICATE_FIELDS, is_valid_predicates), - (_VERSIONS_FIELDS, is_valid_versions), - (_VERSION_FIELDS, is_valid_version)): + (_VERSIONS_FIELDS, is_valid_versions), + (_VERSION_FIELDS, is_valid_version)): for field in fields: value = self[field] if value == 'UNKNOWN': continue if not controller(value): - warnings.append('Wrong value for "%s": %s' \ - % (field, value)) + warnings.append('Wrong value for %r: %s' % (field, value)) return missing, warnings @@ -449,7 +442,6 @@ # # micro-language for PEP 345 environment markers # -_STR_LIMIT = "'\"" # allowed operators _OPERATORS = {'==': lambda x, y: x == y, @@ -466,9 +458,8 @@ # restricted set of variables _VARS = {'sys.platform': sys.platform, - 'python_version': '%s.%s' % (sys.version_info[0], - sys.version_info[1]), - 'python_full_version': sys.version.split()[0], + 'python_version': sys.version[:3], + 'python_full_version': sys.version.split(' ', 1)[0], 'os.name': os.name, 'platform.version': platform.version(), 'platform.machine': platform.machine()} @@ -494,7 +485,7 @@ def _is_string(self, value): if value is None or len(value) < 2: return False - for delimiter in _STR_LIMIT: + for delimiter in '"\'': if value[0] == value[-1] == delimiter: return True return False @@ -505,7 +496,7 @@ def _convert(self, value): if value in _VARS: return self._get_var(value) - return value.strip(_STR_LIMIT) + return value.strip('"\'') def _check_name(self, value): if value not in _VARS: @@ -542,7 +533,7 @@ return self.right is not None def __repr__(self): - return 'OR(%s, %s)' % (repr(self.left), repr(self.right)) + return 'OR(%r, %r)' % (self.left, self.right) def __call__(self): return self.left() or self.right() @@ -557,7 +548,7 @@ return self.right is not None def __repr__(self): - return 'AND(%s, %s)' % (repr(self.left), repr(self.right)) + return 'AND(%r, %r)' % (self.left, self.right) def __call__(self): return self.left() and self.right() diff --git a/src/distutils2/tests/test_core.py b/src/distutils2/tests/test_core.py --- a/src/distutils2/tests/test_core.py +++ b/src/distutils2/tests/test_core.py @@ -64,13 +64,13 @@ f = self.write_setup(setup_using___file__) for s in ['init', 'config', 'commandline', 'run']: distutils2.core.run_setup(f, stop_after=s) - self.assertRaises(ValueError, distutils2.core.run_setup, + self.assertRaises(ValueError, distutils2.core.run_setup, f, stop_after='bob') def test_run_setup_args(self): f = self.write_setup(setup_using___file__) - d = distutils2.core.run_setup(f, script_args=["--help"], - stop_after="init") + d = distutils2.core.run_setup(f, script_args=["--help"], + stop_after="init") self.assertEqual(['--help'], d.script_args) def test_run_setup_uses_current_dir(self): diff --git a/src/distutils2/tests/test_register.py b/src/distutils2/tests/test_register.py --- a/src/distutils2/tests/test_register.py +++ b/src/distutils2/tests/test_register.py @@ -161,7 +161,7 @@ # therefore used afterwards by other commands self.assertEqual(cmd.distribution.password, 'password') - def test_registering(self): + def test_registration(self): # this test runs choice 2 cmd = self._get_cmd() inputs = RawInputs('2', 'tarek', 'tarek at ziade.org') -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:45 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:45 +0200 Subject: [Python-checkins] distutils2: Finish hashlib backport. Message-ID: tarek.ziade pushed 7b8e17e8f4eb to distutils2: http://hg.python.org/distutils2/rev/7b8e17e8f4eb changeset: 419:7b8e17e8f4eb user: ?ric Araujo date: Sat Jul 31 17:46:23 2010 +0200 summary: Finish hashlib backport. files: src/Modules/_hashopenssl.c, src/Modules/md5.c, src/Modules/md5.h, src/Modules/md5module.c, src/Modules/sha256module.c, src/Modules/sha512module.c, src/Modules/shamodule.c, src/distutils2/_backport/_hashopenssl.c, src/distutils2/_backport/hashlib.py, src/distutils2/_backport/md5.c, src/distutils2/_backport/md5.h, src/distutils2/_backport/md5module.c, src/distutils2/_backport/sha256module.c, src/distutils2/_backport/sha512module.c, src/distutils2/_backport/shamodule.c, src/distutils2/_backport/tests/test_pkgutil.py, src/distutils2/command/upload.py, src/setup.py, src/tests.sh diff --git a/src/Modules/_hashopenssl.c b/src/distutils2/_backport/_hashopenssl.c rename from src/Modules/_hashopenssl.c rename to src/distutils2/_backport/_hashopenssl.c diff --git a/src/distutils2/_backport/hashlib.py b/src/distutils2/_backport/hashlib.py --- a/src/distutils2/_backport/hashlib.py +++ b/src/distutils2/_backport/hashlib.py @@ -65,20 +65,20 @@ def __get_builtin_constructor(name): if name in ('SHA1', 'sha1'): - import _sha + from distutils2._backport import _sha return _sha.new elif name in ('MD5', 'md5'): - import _md5 + from distutils2._backport import _md5 return _md5.new elif name in ('SHA256', 'sha256', 'SHA224', 'sha224'): - import _sha256 + from distutils2._backport import _sha256 bs = name[3:] if bs == '256': return _sha256.sha256 elif bs == '224': return _sha256.sha224 elif name in ('SHA512', 'sha512', 'SHA384', 'sha384'): - import _sha512 + from distutils2._backport import _sha512 bs = name[3:] if bs == '512': return _sha512.sha512 @@ -122,7 +122,7 @@ try: - import _hashlib + from distutils2._backport import _hashlib new = __hash_new __get_hash = __get_openssl_constructor except ImportError: diff --git a/src/Modules/md5.c b/src/distutils2/_backport/md5.c rename from src/Modules/md5.c rename to src/distutils2/_backport/md5.c diff --git a/src/Modules/md5.h b/src/distutils2/_backport/md5.h rename from src/Modules/md5.h rename to src/distutils2/_backport/md5.h diff --git a/src/Modules/md5module.c b/src/distutils2/_backport/md5module.c rename from src/Modules/md5module.c rename to src/distutils2/_backport/md5module.c diff --git a/src/Modules/sha256module.c b/src/distutils2/_backport/sha256module.c rename from src/Modules/sha256module.c rename to src/distutils2/_backport/sha256module.c diff --git a/src/Modules/sha512module.c b/src/distutils2/_backport/sha512module.c rename from src/Modules/sha512module.c rename to src/distutils2/_backport/sha512module.c diff --git a/src/Modules/shamodule.c b/src/distutils2/_backport/shamodule.c rename from src/Modules/shamodule.c rename to src/distutils2/_backport/shamodule.c diff --git a/src/distutils2/_backport/tests/test_pkgutil.py b/src/distutils2/_backport/tests/test_pkgutil.py --- a/src/distutils2/_backport/tests/test_pkgutil.py +++ b/src/distutils2/_backport/tests/test_pkgutil.py @@ -10,7 +10,7 @@ try: from hashlib import md5 except ImportError: - from md5 import md5 + from distutils2._backport.hashlib import md5 from test.test_support import run_unittest, TESTFN from distutils2.tests.support import unittest diff --git a/src/distutils2/command/upload.py b/src/distutils2/command/upload.py --- a/src/distutils2/command/upload.py +++ b/src/distutils2/command/upload.py @@ -11,7 +11,7 @@ try: from hashlib import md5 except ImportError: - from md5 import md5 + from distutils2._backport.hashlib import md5 from distutils2.errors import DistutilsOptionError from distutils2.util import spawn diff --git a/src/setup.py b/src/setup.py --- a/src/setup.py +++ b/src/setup.py @@ -168,29 +168,25 @@ # The _hashlib module wraps optimized implementations # of hash functions from the OpenSSL library. - exts.append(Extension('_hashlib', ['_hashopenssl.c'], + exts.append(Extension('distutils2._backport._hashlib', + ['distutils2/_backport/_hashopenssl.c'], include_dirs = [ssl_inc_dir], library_dirs = [os.path.dirname(ssl_lib)], libraries = oslibs[os.name])) else: - exts.append(Extension('_sha', ['shamodule.c']) ) - exts.append(Extension('_md5', - sources=['md5module.c', 'md5.c'], - depends=['md5.h']) ) + exts.append(Extension('distutils2._backport._sha', + ['distutils2/_backport/shamodule.c'])) + exts.append(Extension('distutils2._backport._md5', + sources=['distutils2/_backport/md5module.c', + 'distutils2/_backport/md5.c'], + depends=['distutils2/_backport/md5.h']) ) if (not ssl_lib or openssl_ver < 0x00908000): # OpenSSL doesn't do these until 0.9.8 so we'll bring our own - exts.append(Extension('_sha256', ['sha256module.c'])) - exts.append(Extension('_sha512', ['sha512module.c'])) - - def prepend_modules(filename): - return os.path.join('Modules', filename) - - # all the C code is in the Modules subdirectory, prepend the path - for ext in exts: - ext.sources = [prepend_modules(fn) for fn in ext.sources] - if hasattr(ext, 'depends') and ext.depends is not None: - ext.depends = [prepend_modules(fn) for fn in ext.depends] + exts.append(Extension('distutils2._backport._sha256', + ['distutils2/_backport/sha256module.c'])) + exts.append(Extension('distutils2._backport._sha512', + ['distutils2/_backport/sha512module.c'])) return exts diff --git a/src/tests.sh b/src/tests.sh --- a/src/tests.sh +++ b/src/tests.sh @@ -1,11 +1,11 @@ #!/bin/sh echo -n "Running tests for Python 2.4... " -rm -f _hashlib.so +rm -f distutils2/_backport/_hashlib.so python2.4 setup.py build_ext -q 2> /dev/null > /dev/null python2.4 -Wd runtests.py -q 2> /dev/null if [ $? -ne 0 ];then echo "Failed" - rm -f _hashlib.so + rm -f distutils2/_backport/_hashlib.so exit 1 else echo "Success" -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:45 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:45 +0200 Subject: [Python-checkins] distutils2: Really clean up each and every temp dir or file (with help from Alexis) Message-ID: tarek.ziade pushed 77bd0cb71ecc to distutils2: http://hg.python.org/distutils2/rev/77bd0cb71ecc changeset: 418:77bd0cb71ecc user: ?ric Araujo date: Sat Jul 31 17:30:38 2010 +0200 summary: Really clean up each and every temp dir or file (with help from Alexis) files: src/distutils2/tests/support.py, src/distutils2/tests/test_Mixin2to3.py, src/distutils2/tests/test_pypi_dist.py, src/distutils2/tests/test_pypi_simple.py, src/distutils2/tests/test_upload_docs.py, src/distutils2/tests/test_util.py diff --git a/src/distutils2/tests/support.py b/src/distutils2/tests/support.py --- a/src/distutils2/tests/support.py +++ b/src/distutils2/tests/support.py @@ -93,35 +93,29 @@ class TempdirManager(object): - """TestCase-compatible mixin to handle temporary directories.""" + """TestCase-compatible mixin to create temporary directories and files. + + Directories and files created in a test_* method will be removed after it + has run. + """ def setUp(self): super(TempdirManager, self).setUp() - self.tempdirs = [] - self.tempfiles = [] + self._basetempdir = tempfile.mkdtemp() def tearDown(self): super(TempdirManager, self).tearDown() - while self.tempdirs: - d = self.tempdirs.pop() - shutil.rmtree(d, os.name in ('nt', 'cygwin')) - for file_ in self.tempfiles: - if os.path.exists(file_): - os.remove(file_) + shutil.rmtree(self._basetempdir, os.name in ('nt', 'cygwin')) def mktempfile(self): - """Create a temporary file that will be cleaned up.""" - tempfile_ = tempfile.NamedTemporaryFile() - self.tempfiles.append(tempfile_.name) - return tempfile_ + """Create a read-write temporary file and return it.""" + fd, fn = tempfile.mkstemp(dir=self._basetempdir) + os.close(fd) + return open(fn, 'w+') def mkdtemp(self): - """Create a temporary directory that will be removed on exit. - - Return the path of the directory. - """ - d = tempfile.mkdtemp() - self.tempdirs.append(d) + """Create a temporary directory and return its path.""" + d = tempfile.mkdtemp(dir=self._basetempdir) return d def write_file(self, path, content='xxx'): diff --git a/src/distutils2/tests/test_Mixin2to3.py b/src/distutils2/tests/test_Mixin2to3.py --- a/src/distutils2/tests/test_Mixin2to3.py +++ b/src/distutils2/tests/test_Mixin2to3.py @@ -1,6 +1,5 @@ """Tests for distutils.command.build_py.""" import sys -import tempfile import distutils2 from distutils2.tests import support diff --git a/src/distutils2/tests/test_pypi_dist.py b/src/distutils2/tests/test_pypi_dist.py --- a/src/distutils2/tests/test_pypi_dist.py +++ b/src/distutils2/tests/test_pypi_dist.py @@ -112,24 +112,21 @@ def test_download(self, server): # Download is possible, and the md5 is checked if given - add_to_tmpdirs = lambda x: self.tempdirs.append(os.path.dirname(x)) - url = "%s/simple/foobar/foobar-0.1.tar.gz" % server.full_address # check md5 if given - add_to_tmpdirs(dist.download()) dist = Dist("FooBar", "0.1", url=url, url_hashname="md5", url_hashval="d41d8cd98f00b204e9800998ecf8427e") + dist.download(self.mkdtemp()) # a wrong md5 fails dist2 = Dist("FooBar", "0.1", url=url, url_hashname="md5", url_hashval="wrongmd5") - self.assertRaises(HashDoesNotMatch, dist2.download) - add_to_tmpdirs(dist2.downloaded_location) + self.assertRaises(HashDoesNotMatch, dist2.download, self.mkdtemp()) # we can omit the md5 hash dist3 = Dist("FooBar", "0.1", url=url) - add_to_tmpdirs(dist3.download()) + dist3.download(self.mkdtemp()) # and specify a temporary location # for an already downloaded dist diff --git a/src/distutils2/tests/test_pypi_simple.py b/src/distutils2/tests/test_pypi_simple.py --- a/src/distutils2/tests/test_pypi_simple.py +++ b/src/distutils2/tests/test_pypi_simple.py @@ -4,7 +4,6 @@ import sys import os import shutil -import tempfile import urllib2 from distutils2.pypi import simple diff --git a/src/distutils2/tests/test_upload_docs.py b/src/distutils2/tests/test_upload_docs.py --- a/src/distutils2/tests/test_upload_docs.py +++ b/src/distutils2/tests/test_upload_docs.py @@ -1,13 +1,19 @@ +# -*- encoding: utf8 -*- """Tests for distutils.command.upload_docs.""" -# -*- encoding: utf8 -*- -import httplib, os, os.path, shutil, sys, tempfile, zipfile -from cStringIO import StringIO +import os +import sys +import httplib +import shutil +import zipfile +try: + from cStringIO import StringIO +except ImportError: + from StringIO import StringIO from distutils2.command import upload_docs as upload_docs_mod from distutils2.command.upload_docs import (upload_docs, zip_dir, - encode_multipart) + encode_multipart) from distutils2.core import Distribution - from distutils2.errors import DistutilsFileError, DistutilsOptionError from distutils2.tests import support @@ -59,7 +65,7 @@ self.cmd = upload_docs(self.dist) def test_default_uploaddir(self): - sandbox = tempfile.mkdtemp() + sandbox = self.mkdtemp() previous = os.getcwd() os.chdir(sandbox) try: @@ -72,7 +78,7 @@ def prepare_sample_dir(self, sample_dir=None): if sample_dir is None: - sample_dir = tempfile.mkdtemp() + sample_dir = self.mkdtemp() os.mkdir(os.path.join(sample_dir, "docs")) self.write_file(os.path.join(sample_dir, "docs", "index.html"), "Ce mortel ennui") self.write_file(os.path.join(sample_dir, "index.html"), "Oh la la") diff --git a/src/distutils2/tests/test_util.py b/src/distutils2/tests/test_util.py --- a/src/distutils2/tests/test_util.py +++ b/src/distutils2/tests/test_util.py @@ -4,7 +4,6 @@ from copy import copy from StringIO import StringIO import subprocess -import tempfile import time from distutils2.tests import captured_stdout @@ -301,9 +300,9 @@ def test_newer(self): self.assertRaises(DistutilsFileError, util.newer, 'xxx', 'xxx') - self.newer_f1 = tempfile.NamedTemporaryFile() + self.newer_f1 = self.mktempfile() time.sleep(1) - self.newer_f2 = tempfile.NamedTemporaryFile() + self.newer_f2 = self.mktempfile() self.assertTrue(util.newer(self.newer_f2.name, self.newer_f1.name)) def test_find_packages(self): -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:45 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:45 +0200 Subject: [Python-checkins] distutils2: Don?t use unittest from Python < 3.2. Message-ID: tarek.ziade pushed 6a6b77b52b6e to distutils2: http://hg.python.org/distutils2/rev/6a6b77b52b6e changeset: 421:6a6b77b52b6e user: ?ric Araujo date: Tue Aug 03 16:29:39 2010 +0200 summary: Don?t use unittest from Python < 3.2. files: src/distutils2/tests/support.py diff --git a/src/distutils2/tests/support.py b/src/distutils2/tests/support.py --- a/src/distutils2/tests/support.py +++ b/src/distutils2/tests/support.py @@ -35,8 +35,8 @@ from distutils2 import log from distutils2.log import DEBUG, INFO, WARN, ERROR, FATAL -if sys.version_info >= (2, 7): - # improved unittest package from 2.7's standard library +if sys.version_info >= (3, 2): + # improved unittest package from 3.2's standard library import unittest else: # external release of same package for older versions -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:45 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:45 +0200 Subject: [Python-checkins] distutils2: pep8 compliance Message-ID: tarek.ziade pushed 0f61b7399cfa to distutils2: http://hg.python.org/distutils2/rev/0f61b7399cfa changeset: 428:0f61b7399cfa user: Josip Djolonga date: Sat Jul 17 18:05:21 2010 +0200 summary: pep8 compliance files: src/distutils2/command/install_distinfo.py diff --git a/src/distutils2/command/install_distinfo.py b/src/distutils2/command/install_distinfo.py --- a/src/distutils2/command/install_distinfo.py +++ b/src/distutils2/command/install_distinfo.py @@ -40,7 +40,7 @@ 'requested', 'no-dist-record', ] - + negative_opt = {'no-requested': 'requested'} def initialize_options(self): @@ -54,11 +54,12 @@ ('distinfo_dir', 'distinfo_dir'), ('installer', 'installer'), ('requested', 'requested'), - ('no_distinfo_record', 'no_distinfo_record')) + ('no_distinfo_record', + 'no_distinfo_record')) self.set_undefined_options('install_lib', ('install_dir', 'distinfo_dir')) - + if self.installer is None: self.installer = 'distutils' if self.requested is None: -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:45 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:45 +0200 Subject: [Python-checkins] distutils2: Use explicit name instead of literal (Tarek?s preference) Message-ID: tarek.ziade pushed d6315cf22846 to distutils2: http://hg.python.org/distutils2/rev/d6315cf22846 changeset: 422:d6315cf22846 user: ?ric Araujo date: Tue Aug 03 17:00:02 2010 +0200 summary: Use explicit name instead of literal (Tarek?s preference) files: src/distutils2/_backport/tests/__init__.py, src/distutils2/command/build_ext.py, src/distutils2/mkpkg.py, src/distutils2/util.py diff --git a/src/distutils2/_backport/tests/__init__.py b/src/distutils2/_backport/tests/__init__.py --- a/src/distutils2/_backport/tests/__init__.py +++ b/src/distutils2/_backport/tests/__init__.py @@ -4,7 +4,7 @@ from distutils2.tests.support import unittest -here = os.path.dirname(__file__) or '.' +here = os.path.dirname(__file__) or os.curdir def test_suite(): suite = unittest.TestSuite() diff --git a/src/distutils2/command/build_ext.py b/src/distutils2/command/build_ext.py --- a/src/distutils2/command/build_ext.py +++ b/src/distutils2/command/build_ext.py @@ -292,7 +292,7 @@ "config")) else: # building python standard extensions - self.library_dirs.append('.') + self.library_dirs.append(os.curdir) # for extensions under Linux or Solaris with a shared Python library, # Python's library directory must be appended to library_dirs @@ -305,7 +305,7 @@ self.library_dirs.append(sysconfig.get_config_var('LIBDIR')) else: # building python standard extensions - self.library_dirs.append('.') + self.library_dirs.append(os.curdir) # The argument parsing will result in self.define being a string, but # it has to be a list of 2-tuples. All the preprocessor symbols diff --git a/src/distutils2/mkpkg.py b/src/distutils2/mkpkg.py --- a/src/distutils2/mkpkg.py +++ b/src/distutils2/mkpkg.py @@ -737,14 +737,14 @@ self.setupData['name'] = m.group(1) self.setupData['version'] = m.group(2) - for root, dirs, files in os.walk('.'): + for root, dirs, files in os.walk(os.curdir): for file in files: - if root == '.' and file == 'setup.py': continue + if root == os.curdir and file == 'setup.py': continue fileName = os.path.join(root, file) self.inspectFile(fileName) if file == '__init__.py': - trySrc = os.path.join('.', 'src') + trySrc = os.path.join(os.curdir, 'src') tmpRoot = root if tmpRoot.startswith(trySrc): tmpRoot = tmpRoot[len(trySrc):] diff --git a/src/distutils2/util.py b/src/distutils2/util.py --- a/src/distutils2/util.py +++ b/src/distutils2/util.py @@ -84,8 +84,8 @@ raise ValueError("path '%s' cannot end with '/'" % pathname) paths = pathname.split('/') - while '.' in paths: - paths.remove('.') + while os.curdir in paths: + paths.remove(os.curdir) if not paths: return os.curdir return os.path.join(*paths) @@ -586,7 +586,7 @@ return path[len(root_path) + 1:].replace(os.sep, '.') -def find_packages(paths=('.',), exclude=()): +def find_packages(paths=(os.curdir,), exclude=()): """Return a list all Python packages found recursively within directories 'paths' -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:45 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:45 +0200 Subject: [Python-checkins] distutils2: changed name from install_dist_info to install_distinfo for consistency Message-ID: tarek.ziade pushed 600504efa42b to distutils2: http://hg.python.org/distutils2/rev/600504efa42b changeset: 427:600504efa42b user: Josip Djolonga date: Sat Jul 17 18:02:01 2010 +0200 summary: changed name from install_dist_info to install_distinfo for consistency files: src/distutils2/command/install.py, src/distutils2/command/install_dist_info.py, src/distutils2/command/install_distinfo.py diff --git a/src/distutils2/command/install.py b/src/distutils2/command/install.py --- a/src/distutils2/command/install.py +++ b/src/distutils2/command/install.py @@ -79,15 +79,26 @@ ('record=', None, "filename in which to record list of installed files"), + + # .dist-info related arguments, read by install_dist_info + ('no-distinfo', None, 'do not create a .dist-info directory'), + ('distinfo-dir=', None, + 'directory where the the .dist-info directory will ' + 'be installed'), + ('installer=', None, 'the name of the installer'), + ('requested', None, 'generate a REQUESTED file'), + ('no-requested', None, 'do not generate a REQUESTED file'), + ('no-distinfo-record', None, 'do not generate a RECORD file'), ] - boolean_options = ['compile', 'force', 'skip-build'] + boolean_options = ['compile', 'force', 'skip-build', 'no-dist-info', + 'requested', 'no-dist-record',] user_options.append(('user', None, "install in user site-package '%s'" % \ get_path('purelib', '%s_user' % os.name))) boolean_options.append('user') - negative_opt = {'no-compile' : 'compile'} + negative_opt = {'no-compile' : 'compile', 'no-requested': 'requested'} def initialize_options(self): @@ -159,6 +170,13 @@ #self.install_info = None self.record = None + + # .dist-info related options + self.no_distinfo = None + self.distinfo_dir = None + self.installer = None + self.requested = None + self.no_distinfo_record = None # -- Option finalizing methods ------------------------------------- @@ -305,6 +323,9 @@ # Punt on doc directories for now -- after all, we're punting on # documentation completely! + + if self.no_distinfo is None: + self.no_distinfo = False def dump_dirs(self, msg): """Dumps the list of user options.""" @@ -586,7 +607,7 @@ ('install_headers', has_headers), ('install_scripts', has_scripts), ('install_data', has_data), - # keep install_dist_info last, as it needs the record + # keep install_distinfo last, as it needs the record # with files to be completely generated - ('install_dist_info', lambda self:True), + ('install_distinfo', lambda self: not self.no_distinfo), ] diff --git a/src/distutils2/command/install_dist_info.py b/src/distutils2/command/install_distinfo.py rename from src/distutils2/command/install_dist_info.py rename to src/distutils2/command/install_distinfo.py --- a/src/distutils2/command/install_dist_info.py +++ b/src/distutils2/command/install_distinfo.py @@ -1,51 +1,71 @@ """ -distutils.command.install_dist_info -=================================== +distutils.command.install_distinfo +================================== :Author: Josip Djolonga + +This module implements the ``install_distinfo`` command that creates the +``.dist-info`` directory for the distribution, as specified in :pep:`376`. +Usually, you do not have to call this command directly, it gets called +automatically by the ``install`` command. """ from distutils2.command.cmd import Command from distutils2 import log from distutils2._backport.shutil import rmtree + import csv import hashlib import os import re - -class install_dist_info(Command): + +class install_distinfo(Command): """Install a .dist-info directory for the package""" description = 'Install a .dist-info directory for the package' user_options = [ - ('dist-info-dir=', None, - 'directory to install the .dist-info directory to'), + ('dist-info-dir=', None, + 'directory where the the .dist-info directory will ' + 'be installed'), ('installer=', None, 'the name of the installer'), - ('no-dist-requested', None, 'do not generate a REQUESTED file'), + ('requested', None, 'generate a REQUESTED file'), + ('no-requested', None, 'do not generate a REQUESTED file'), ('no-dist-record', None, 'do not generate a RECORD file'), ] - + boolean_options = [ - 'no-dist-requested', + 'requested', 'no-dist-record', ] + + negative_opt = {'no-requested': 'requested'} def initialize_options(self): - self.dist_info_dir = None + self.distinfo_dir = None self.installer = None - self.no_dist_requested = False - self.no_dist_record = False + self.requested = None + self.no_distinfo_record = None def finalize_options(self): + self.set_undefined_options('install', + ('distinfo_dir', 'distinfo_dir'), + ('installer', 'installer'), + ('requested', 'requested'), + ('no_distinfo_record', 'no_distinfo_record')) + self.set_undefined_options('install_lib', - ('install_dir', 'dist_info_dir')) + ('install_dir', 'distinfo_dir')) + + if self.installer is None: + self.installer = 'distutils' + if self.requested is None: + self.requested = True + if self.no_distinfo_record is None: + self.no_distinfo_record = False - if self.installer is None: - self.installer = 'distribute' - metadata = self.distribution.metadata basename = "%s-%s.dist-info" % ( @@ -53,67 +73,71 @@ to_filename(safe_version(metadata['Version'])), ) - self.distinfo_dir = os.path.join(self.dist_info_dir, basename) + self.distinfo_dir = os.path.join(self.distinfo_dir, basename) self.outputs = [] def run(self): - target = self.distinfo_dir - if os.path.isdir(target) and not os.path.islink(target) \ - and not self.dry_run: - rmtree(target) - elif os.path.exists(target): - self.execute(os.unlink, (self.distinfo_dir,), "Removing " + target) + if not self.dry_run: + target = self.distinfo_dir - if not self.dry_run: + if os.path.isdir(target) and not os.path.islink(target): + rmtree(target) + elif os.path.exists(target): + self.execute(os.unlink, (self.distinfo_dir,), + "Removing " + target) + self.execute(os.makedirs, (target,), "Creating " + target) metadata_path = os.path.join(self.distinfo_dir, 'METADATA') log.info('Creating %s' % (metadata_path,)) self.distribution.metadata.write(metadata_path) self.outputs.append(metadata_path) - + installer_path = os.path.join(self.distinfo_dir, 'INSTALLER') log.info('Creating %s' % (installer_path,)) f = open(installer_path, 'w') - f.write(self.installer) - f.close() + try: + f.write(self.installer) + finally: + f.close() self.outputs.append(installer_path) - if not self.no_dist_requested: + if self.requested: requested_path = os.path.join(self.distinfo_dir, 'REQUESTED') log.info('Creating %s' % (requested_path,)) f = open(requested_path, 'w') f.close() self.outputs.append(requested_path) - if not self.no_dist_record: + if not self.no_distinfo_record: record_path = os.path.join(self.distinfo_dir, 'RECORD') log.info('Creating %s' % (record_path,)) f = open(record_path, 'wb') - writer = csv.writer(f, delimiter=',', - lineterminator=os.linesep, - quotechar='"') - - install = self.get_finalized_command('install') - - for fpath in install.get_outputs(): - if fpath.endswith('.pyc') or fpath.endswith('.pyo'): - # do not put size and md5 hash, as in PEP-376 - writer.writerow((fpath, '', '')) - else: - size = os.path.getsize(fpath) - fd = open(fpath, 'r') - hash = hashlib.md5() - hash.update(fd.read()) - md5sum = hash.hexdigest() - writer.writerow((fpath, md5sum, size)) - - # add the RECORD file itself - writer.writerow((record_path, '', '')) - self.outputs.append(record_path) + try: + writer = csv.writer(f, delimiter=',', + lineterminator=os.linesep, + quotechar='"') - f.close() - + install = self.get_finalized_command('install') + + for fpath in install.get_outputs(): + if fpath.endswith('.pyc') or fpath.endswith('.pyo'): + # do not put size and md5 hash, as in PEP-376 + writer.writerow((fpath, '', '')) + else: + size = os.path.getsize(fpath) + fd = open(fpath, 'r') + hash = hashlib.md5() + hash.update(fd.read()) + md5sum = hash.hexdigest() + writer.writerow((fpath, md5sum, size)) + + # add the RECORD file itself + writer.writerow((record_path, '', '')) + self.outputs.append(record_path) + finally: + f.close() + def get_outputs(self): return self.outputs @@ -122,6 +146,7 @@ # can be replaced by importing them from pkg_resources once it is included # in the stdlib. + def safe_name(name): """Convert an arbitrary string to a standard distribution name @@ -136,7 +161,7 @@ Spaces become dots, and all other non-alphanumeric characters become dashes, with runs of multiple dashes condensed to a single dash. """ - version = version.replace(' ','.') + version = version.replace(' ', '.') return re.sub('[^A-Za-z0-9.]+', '-', version) @@ -145,4 +170,4 @@ Any '-' characters are currently replaced with '_'. """ - return name.replace('-','_') + return name.replace('-', '_') -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:45 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:45 +0200 Subject: [Python-checkins] distutils2: Replaced install_egg_info with install_dist_info Message-ID: tarek.ziade pushed 6cf9c35e690e to distutils2: http://hg.python.org/distutils2/rev/6cf9c35e690e changeset: 425:6cf9c35e690e user: Josip Djolonga date: Fri Jul 16 18:00:05 2010 +0200 summary: Replaced install_egg_info with install_dist_info files: src/distutils2/_backport/tests/fake_dists/grammar-1.0a4.dist-info/METADATA, src/distutils2/command/install.py, src/distutils2/command/install_dist_info.py, src/distutils2/command/install_egg_info.py diff --git a/src/distutils2/_backport/tests/fake_dists/grammar-1.0a4.dist-info/METADATA b/src/distutils2/_backport/tests/fake_dists/grammar-1.0a4.dist-info/METADATA --- a/src/distutils2/_backport/tests/fake_dists/grammar-1.0a4.dist-info/METADATA +++ b/src/distutils2/_backport/tests/fake_dists/grammar-1.0a4.dist-info/METADATA @@ -2,3 +2,4 @@ Name: grammar Version: 1.0a4 Requires-Dist: truffles (>=1.2) +Author: Sherlock Holmes diff --git a/src/distutils2/command/install.py b/src/distutils2/command/install.py --- a/src/distutils2/command/install.py +++ b/src/distutils2/command/install.py @@ -586,5 +586,7 @@ ('install_headers', has_headers), ('install_scripts', has_scripts), ('install_data', has_data), - ('install_egg_info', lambda self:True), + # keep install_dist_info last, as it needs the record + # with files to be completely generated + ('install_dist_info', lambda self:True), ] diff --git a/src/distutils2/command/install_dist_info.py b/src/distutils2/command/install_dist_info.py new file mode 100644 --- /dev/null +++ b/src/distutils2/command/install_dist_info.py @@ -0,0 +1,146 @@ +""" +distutils.command.install_dist_info +=================================== + +:Author: Josip Djolonga +""" + +from distutils2.command.cmd import Command +from distutils2 import log +from distutils2._backport.shutil import rmtree + +import csv +import hashlib +import os +import re + + +class install_dist_info(Command): + """Install a .dist-info directory for the package""" + + description = 'Install a .dist-info directory for the package' + + user_options = [ + ('dist-info-dir=', None, + 'directory to install the .dist-info directory to'), + ('installer=', None, 'the name of the installer'), + ('no-dist-requested', None, 'do not generate a REQUESTED file'), + ('no-dist-record', None, 'do not generate a RECORD file'), + ] + + boolean_options = [ + 'no-dist-requested', + 'no-dist-record', + ] + + def initialize_options(self): + self.dist_info_dir = None + self.installer = None + self.no_dist_requested = False + self.no_dist_record = False + + def finalize_options(self): + self.set_undefined_options('install_lib', + ('install_dir', 'dist_info_dir')) + + if self.installer is None: + self.installer = 'distribute' + + metadata = self.distribution.metadata + + basename = "%s-%s.dist-info" % ( + to_filename(safe_name(metadata['Name'])), + to_filename(safe_version(metadata['Version'])), + ) + + self.distinfo_dir = os.path.join(self.dist_info_dir, basename) + self.outputs = [] + + def run(self): + target = self.distinfo_dir + if os.path.isdir(target) and not os.path.islink(target) \ + and not self.dry_run: + rmtree(target) + elif os.path.exists(target): + self.execute(os.unlink, (self.distinfo_dir,), "Removing " + target) + + if not self.dry_run: + self.execute(os.makedirs, (target,), "Creating " + target) + + metadata_path = os.path.join(self.distinfo_dir, 'METADATA') + log.info('Creating %s' % (metadata_path,)) + self.distribution.metadata.write(metadata_path) + self.outputs.append(metadata_path) + + installer_path = os.path.join(self.distinfo_dir, 'INSTALLER') + log.info('Creating %s' % (installer_path,)) + f = open(installer_path, 'w') + f.write(self.installer) + f.close() + self.outputs.append(installer_path) + + if not self.no_dist_requested: + requested_path = os.path.join(self.distinfo_dir, 'REQUESTED') + log.info('Creating %s' % (requested_path,)) + f = open(requested_path, 'w') + f.close() + self.outputs.append(requested_path) + + if not self.no_dist_record: + record_path = os.path.join(self.distinfo_dir, 'RECORD') + log.info('Creating %s' % (record_path,)) + f = open(record_path, 'wb') + writer = csv.writer(f, delimiter=',', + lineterminator=os.linesep, + quotechar='"') + + install = self.get_finalized_command('install') + + for fpath in install.get_outputs(): + if fpath.endswith('.pyc'): + continue + # FIXME, in get_outputs() missing .pyc files exist + size = os.path.getsize(fpath) + fd = open(fpath, 'r') + hash = hashlib.md5() + hash.update(fd.read()) + md5sum = hash.hexdigest() + writer.writerow((fpath, md5sum, size)) + + writer.writerow((record_path, '', '')) + self.outputs.append(record_path) + + f.close() + + def get_outputs(self): + return self.outputs + + +# The following routines are taken from setuptools' pkg_resources module and +# can be replaced by importing them from pkg_resources once it is included +# in the stdlib. + +def safe_name(name): + """Convert an arbitrary string to a standard distribution name + + Any runs of non-alphanumeric/. characters are replaced with a single '-'. + """ + return re.sub('[^A-Za-z0-9.]+', '-', name) + + +def safe_version(version): + """Convert an arbitrary string to a standard version string + + Spaces become dots, and all other non-alphanumeric characters become + dashes, with runs of multiple dashes condensed to a single dash. + """ + version = version.replace(' ','.') + return re.sub('[^A-Za-z0-9.]+', '-', version) + + +def to_filename(name): + """Convert a project or version name to its filename-escaped form + + Any '-' characters are currently replaced with '_'. + """ + return name.replace('-','_') diff --git a/src/distutils2/command/install_egg_info.py b/src/distutils2/command/install_egg_info.py deleted file mode 100644 --- a/src/distutils2/command/install_egg_info.py +++ /dev/null @@ -1,83 +0,0 @@ -"""distutils.command.install_egg_info - -Implements the Distutils 'install_egg_info' command, for installing -a package's PKG-INFO metadata.""" - - -from distutils2.command.cmd import Command -from distutils2 import log -from distutils2._backport.shutil import rmtree -import os, sys, re - -class install_egg_info(Command): - """Install an .egg-info file for the package""" - - description = "Install package's PKG-INFO metadata as an .egg-info file" - user_options = [ - ('install-dir=', 'd', "directory to install to"), - ] - - def initialize_options(self): - self.install_dir = None - - def finalize_options(self): - metadata = self.distribution.metadata - self.set_undefined_options('install_lib',('install_dir','install_dir')) - basename = "%s-%s-py%s.egg-info" % ( - to_filename(safe_name(metadata['Name'])), - to_filename(safe_version(metadata['Version'])), - sys.version[:3] - ) - self.target = os.path.join(self.install_dir, basename) - self.outputs = [self.target] - - def run(self): - target = self.target - if os.path.isdir(target) and not os.path.islink(target): - if self.dry_run: - pass # XXX - else: - rmtree(target) - elif os.path.exists(target): - self.execute(os.unlink,(self.target,),"Removing "+target) - elif not os.path.isdir(self.install_dir): - self.execute(os.makedirs, (self.install_dir,), - "Creating "+self.install_dir) - log.info("Writing %s", target) - if not self.dry_run: - f = open(target, 'w') - self.distribution.metadata.write_file(f) - f.close() - - def get_outputs(self): - return self.outputs - - -# The following routines are taken from setuptools' pkg_resources module and -# can be replaced by importing them from pkg_resources once it is included -# in the stdlib. - -def safe_name(name): - """Convert an arbitrary string to a standard distribution name - - Any runs of non-alphanumeric/. characters are replaced with a single '-'. - """ - return re.sub('[^A-Za-z0-9.]+', '-', name) - - -def safe_version(version): - """Convert an arbitrary string to a standard version string - - Spaces become dots, and all other non-alphanumeric characters become - dashes, with runs of multiple dashes condensed to a single dash. - """ - version = version.replace(' ','.') - return re.sub('[^A-Za-z0-9.]+', '-', version) - - -def to_filename(name): - """Convert a project or version name to its filename-escaped form - - Any '-' characters are currently replaced with '_'. - """ - return name.replace('-','_') -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:45 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:45 +0200 Subject: [Python-checkins] distutils2: Basic caching support added to pkgutil Message-ID: tarek.ziade pushed 969b80c7655a to distutils2: http://hg.python.org/distutils2/rev/969b80c7655a changeset: 423:969b80c7655a parent: 214:d5d979a9145a user: Josip Djolonga date: Tue Jun 22 19:16:11 2010 +0200 summary: Basic caching support added to pkgutil files: src/distutils2/_backport/pkgutil.py diff --git a/src/distutils2/_backport/pkgutil.py b/src/distutils2/_backport/pkgutil.py --- a/src/distutils2/_backport/pkgutil.py +++ b/src/distutils2/_backport/pkgutil.py @@ -29,7 +29,6 @@ 'provides_distribution', 'obsoletes_distribution', ] - def read_code(stream): # This helper is needed in order for the :pep:`302` emulation to # correctly handle compiled files @@ -613,6 +612,49 @@ DIST_FILES = ('INSTALLER', 'METADATA', 'RECORD', 'REQUESTED',) +# Cache +_cache_name = {} # maps names to Distribution instances +_cache_name_egg = {} # maps names to EggInfoDistribution instances +_cache_path = {} # maps paths to Distribution instances +_cache_path_egg = {} # maps paths to EggInfoDistribution instances +_cache_generated = False + +def clear_cache(): + global _cache_name, _cache_name_egg, cache_path, _cache_path_egg, \ + _cache_generated + + _cache_name = {} + _cache_name_egg = {} + _cache_path = {} + _cache_path_egg = {} + _cache_generated = False + +def _generate_cache(): + clear_cache() + + global _cache_generated + + for path in sys.path: + realpath = os.path.realpath(path) + if not os.path.isdir(realpath): + continue + for dir in os.listdir(realpath): + dist_path = os.path.join(realpath, dir) + if dir.endswith('.dist-info'): + dist = Distribution(dist_path) + _cache_path[dist_path] = dist + if not dist.name in _cache_name: + _cache_name[dist.name] = [] + _cache_name[dist.name].append(dist) + elif dir.endswith('.egg-info') or dir.endswith('.egg'): + dist = EggInfoDistribution(dist_path) + _cache_path_egg[dist_path] = dist + if not dist.name in _cache_name_egg: + _cache_name_egg[dist.name] = [] + _cache_name_egg[dist.name].append(dist) + _cache_generated = True + + class Distribution(object): """Created with the *path* of the ``.dist-info`` directory provided to the @@ -631,11 +673,18 @@ (in other words, whether the package was installed by user request).""" def __init__(self, path): + if path in _cache_path: + self.metadata = _cache_path[path].metadata + else: + metadata_path = os.path.join(path, 'METADATA') + self.metadata = DistributionMetadata(path=metadata_path) + self.path = path - metadata_path = os.path.join(path, 'METADATA') - self.metadata = DistributionMetadata(path=metadata_path) self.name = self.metadata['name'] + if not path in _cache_path: + _cache_path[path] = self + def _get_records(self, local=False): RECORD = os.path.join(self.path, 'RECORD') record_reader = csv_reader(open(RECORD, 'rb'), delimiter=',') @@ -753,6 +802,11 @@ def __init__(self, path): self.path = path + if path in _cache_path_egg: + self.metadata = _cache_path_egg[path].metadata + self.name = self.metadata['Name'] + return + # reused from Distribute's pkg_resources def yield_lines(strs): """Yield non-empty/non-comment lines of a ``basestring`` or sequence""" @@ -784,7 +838,7 @@ requires = zipf.get_data('EGG-INFO/requires.txt') except IOError: requires = None - self.name = self.metadata['name'] + self.name = self.metadata['Name'] elif path.endswith('.egg-info'): if os.path.isdir(path): path = os.path.join(path, 'PKG-INFO') @@ -837,6 +891,8 @@ else: self.metadata['Requires'] += reqs + _cache_path_egg[path] = self + def get_installed_files(self, local=False): return [] @@ -847,13 +903,11 @@ return isinstance(other, EggInfoDistribution) and \ self.path == other.path - def _normalize_dist_name(name): """Returns a normalized name from the given *name*. :rtype: string""" return name.replace('-', '_') - def distinfo_dirname(name, version): """ The *name* and *version* parameters are converted into their @@ -892,19 +946,13 @@ :rtype: iterator of :class:`Distribution` and :class:`EggInfoDistribution` instances""" - for path in sys.path: - realpath = os.path.realpath(path) - if not os.path.isdir(realpath): - continue - for dir in os.listdir(realpath): - if dir.endswith('.dist-info'): - dist = Distribution(os.path.join(realpath, dir)) - yield dist - elif use_egg_info and (dir.endswith('.egg-info') or - dir.endswith('.egg')): - dist = EggInfoDistribution(os.path.join(realpath, dir)) - yield dist - + if not _cache_generated: + _generate_cache() + for dist in _cache_path.itervalues(): + yield dist + if use_egg_info: + for dist in _cache_path_egg.itervalues(): + yield dist def get_distribution(name, use_egg_info=False): """ @@ -922,17 +970,15 @@ value is expected. If the directory is not found, ``None`` is returned. :rtype: :class:`Distribution` or :class:`EggInfoDistribution` or None""" - found = None - for dist in get_distributions(): - if dist.name == name: - found = dist - break - if use_egg_info: - for dist in get_distributions(True): - if dist.name == name: - found = dist - break - return found + if not _cache_generated: + _generate_cache() + + if name in _cache_name: + return _cache_name[name][0] + elif use_egg_info and name in _cache_name_egg: + return _cache_name_egg[name][0] + else: + return None def obsoletes_distribution(name, version=None, use_egg_info=False): -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:45 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:45 +0200 Subject: [Python-checkins] distutils2: fixed the writer to handle .pyc files in RECORD Message-ID: tarek.ziade pushed 753e2c57a71d to distutils2: http://hg.python.org/distutils2/rev/753e2c57a71d changeset: 426:753e2c57a71d user: Josip Djolonga date: Sat Jul 17 14:31:47 2010 +0200 summary: fixed the writer to handle .pyc files in RECORD files: src/distutils2/command/install_dist_info.py diff --git a/src/distutils2/command/install_dist_info.py b/src/distutils2/command/install_dist_info.py --- a/src/distutils2/command/install_dist_info.py +++ b/src/distutils2/command/install_dist_info.py @@ -97,19 +97,21 @@ install = self.get_finalized_command('install') for fpath in install.get_outputs(): - if fpath.endswith('.pyc'): - continue - # FIXME, in get_outputs() missing .pyc files exist - size = os.path.getsize(fpath) - fd = open(fpath, 'r') - hash = hashlib.md5() - hash.update(fd.read()) - md5sum = hash.hexdigest() - writer.writerow((fpath, md5sum, size)) - + if fpath.endswith('.pyc') or fpath.endswith('.pyo'): + # do not put size and md5 hash, as in PEP-376 + writer.writerow((fpath, '', '')) + else: + size = os.path.getsize(fpath) + fd = open(fpath, 'r') + hash = hashlib.md5() + hash.update(fd.read()) + md5sum = hash.hexdigest() + writer.writerow((fpath, md5sum, size)) + + # add the RECORD file itself writer.writerow((record_path, '', '')) self.outputs.append(record_path) - + f.close() def get_outputs(self): -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:45 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:45 +0200 Subject: [Python-checkins] distutils2: Improved the caching functionality of pkgutil, added some docs Message-ID: tarek.ziade pushed cb07b3bec9ec to distutils2: http://hg.python.org/distutils2/rev/cb07b3bec9ec changeset: 424:cb07b3bec9ec user: Josip Djolonga date: Thu Jun 24 03:23:26 2010 +0200 summary: Improved the caching functionality of pkgutil, added some docs files: docs/source/pkgutil.rst, src/distutils2/_backport/pkgutil.py diff --git a/docs/source/pkgutil.rst b/docs/source/pkgutil.rst --- a/docs/source/pkgutil.rst +++ b/docs/source/pkgutil.rst @@ -17,6 +17,16 @@ first a complete documentation of the functions and classes is provided and then several use cases are presented. +Caching ++++++++ + +For performance purposes, the list of distributions is being internally +cached. It is enabled by default, but you can turn it off or clear +it using +:func:`distutils2._backport.pkgutil.set_cache_enabled` and +:func:`distutils2._backport.pkgutil.clear_cache`. + + API Reference ============= diff --git a/src/distutils2/_backport/pkgutil.py b/src/distutils2/_backport/pkgutil.py --- a/src/distutils2/_backport/pkgutil.py +++ b/src/distutils2/_backport/pkgutil.py @@ -20,15 +20,18 @@ import re import warnings + __all__ = [ 'get_importer', 'iter_importers', 'get_loader', 'find_loader', 'walk_packages', 'iter_modules', 'get_data', 'ImpImporter', 'ImpLoader', 'read_code', 'extend_path', 'Distribution', 'EggInfoDistribution', 'distinfo_dirname', 'get_distributions', 'get_distribution', 'get_file_users', - 'provides_distribution', 'obsoletes_distribution', + 'provides_distribution', 'obsoletes_distribution', 'set_cache_enabled', + 'clear_cache' ] + def read_code(stream): # This helper is needed in order for the :pep:`302` emulation to # correctly handle compiled files @@ -186,8 +189,8 @@ searches the current ``sys.path``, plus any modules that are frozen or built-in. - Note that :class:`ImpImporter` does not currently support being used by placement - on ``sys.meta_path``. + Note that :class:`ImpImporter` does not currently support being used by + placement on ``sys.meta_path``. """ def __init__(self, path=None): @@ -576,7 +579,8 @@ argument should be the name of a package, in standard module format (``foo.bar``). The resource argument should be in the form of a relative filename, using ``'/'`` as the path separator. The parent directory name - ``'..'`` is not allowed, and nor is a rooted name (starting with a ``'/'``). + ``'..'`` is not allowed, and nor is a rooted name (starting with a + ``'/'``). The function returns a binary string, which is the contents of the specified resource. @@ -617,43 +621,84 @@ _cache_name_egg = {} # maps names to EggInfoDistribution instances _cache_path = {} # maps paths to Distribution instances _cache_path_egg = {} # maps paths to EggInfoDistribution instances -_cache_generated = False +_cache_generated = False # indicates if .dist-info distributions are cached +_cache_generated_egg = False # indicates if .dist-info and .egg are cached +_cache_enabled = True + + +def set_cache_enabled(flag): + """ + Enables or disables the internal cache depending on *flag*. + + Note that this function will not clear the cache in any case, for that + functionality see :func:`clear_cache`. + + :parameter flag: + :type flag: boolean + """ + global _cache_enabled + + _cache_enabled = flag + def clear_cache(): + """ Clears the internal cache. """ global _cache_name, _cache_name_egg, cache_path, _cache_path_egg, \ - _cache_generated + _cache_generated, _cache_generated_egg _cache_name = {} _cache_name_egg = {} _cache_path = {} _cache_path_egg = {} _cache_generated = False + _cache_generated_egg = False -def _generate_cache(): - clear_cache() - global _cache_generated +def _yield_distributions(dist, egg): + """ + Yield .dist-info and .egg(-info) distributions, based on the arguments + :parameter dist: yield .dist-info distributions + :parameter egg: yield .egg(-info) distributions + """ for path in sys.path: realpath = os.path.realpath(path) if not os.path.isdir(realpath): continue for dir in os.listdir(realpath): dist_path = os.path.join(realpath, dir) - if dir.endswith('.dist-info'): - dist = Distribution(dist_path) - _cache_path[dist_path] = dist + if dist and dir.endswith('.dist-info'): + yield Distribution(dist_path) + elif egg and (dir.endswith('.egg-info') or + dir.endswith('.egg')): + yield EggInfoDistribution(dist_path) + + +def _generate_cache(use_egg_info=False): + global _cache_generated, _cache_generated_egg + + if _cache_generated_egg or (_cache_generated and not use_egg_info): + return + else: + gen_dist = not _cache_generated + gen_egg = use_egg_info + + for dist in _yield_distributions(gen_dist, gen_egg): + if isinstance(dist, Distribution): + _cache_path[dist.path] = dist if not dist.name in _cache_name: _cache_name[dist.name] = [] _cache_name[dist.name].append(dist) - elif dir.endswith('.egg-info') or dir.endswith('.egg'): - dist = EggInfoDistribution(dist_path) - _cache_path_egg[dist_path] = dist + else: + _cache_path_egg[dist.path] = dist if not dist.name in _cache_name_egg: _cache_name_egg[dist.name] = [] _cache_name_egg[dist.name].append(dist) - _cache_generated = True + if gen_dist: + _cache_generated = True + if gen_egg: + _cache_generated_egg = True class Distribution(object): @@ -669,11 +714,12 @@ """A :class:`distutils2.metadata.DistributionMetadata` instance loaded with the distribution's ``METADATA`` file.""" requested = False - """A boolean that indicates whether the ``REQUESTED`` metadata file is present - (in other words, whether the package was installed by user request).""" + """A boolean that indicates whether the ``REQUESTED`` metadata file is + present (in other words, whether the package was installed by user + request).""" def __init__(self, path): - if path in _cache_path: + if _cache_enabled and path in _cache_path: self.metadata = _cache_path[path].metadata else: metadata_path = os.path.join(path, 'METADATA') @@ -682,7 +728,7 @@ self.path = path self.name = self.metadata['name'] - if not path in _cache_path: + if _cache_enabled and not path in _cache_path: _cache_path[path] = self def _get_records(self, local=False): @@ -802,7 +848,7 @@ def __init__(self, path): self.path = path - if path in _cache_path_egg: + if _cache_enabled and path in _cache_path_egg: self.metadata = _cache_path_egg[path].metadata self.name = self.metadata['Name'] return @@ -891,7 +937,8 @@ else: self.metadata['Requires'] += reqs - _cache_path_egg[path] = self + if _cache_enabled: + _cache_path_egg[self.path] = self def get_installed_files(self, local=False): return [] @@ -903,11 +950,13 @@ return isinstance(other, EggInfoDistribution) and \ self.path == other.path + def _normalize_dist_name(name): """Returns a normalized name from the given *name*. :rtype: string""" return name.replace('-', '_') + def distinfo_dirname(name, version): """ The *name* and *version* parameters are converted into their @@ -946,13 +995,19 @@ :rtype: iterator of :class:`Distribution` and :class:`EggInfoDistribution` instances""" - if not _cache_generated: - _generate_cache() - for dist in _cache_path.itervalues(): - yield dist - if use_egg_info: - for dist in _cache_path_egg.itervalues(): + if not _cache_enabled: + for dist in _yield_distributions(use_egg_info): yield dist + else: + _generate_cache(use_egg_info) + + for dist in _cache_path.itervalues(): + yield dist + + if use_egg_info: + for dist in _cache_path_egg.itervalues(): + yield dist + def get_distribution(name, use_egg_info=False): """ @@ -970,15 +1025,19 @@ value is expected. If the directory is not found, ``None`` is returned. :rtype: :class:`Distribution` or :class:`EggInfoDistribution` or None""" - if not _cache_generated: - _generate_cache() + if not _cache_enabled: + for dist in _yield_distributions(use_egg_info): + if dist.name == name: + return dist + else: + _generate_cache(use_egg_info) - if name in _cache_name: - return _cache_name[name][0] - elif use_egg_info and name in _cache_name_egg: - return _cache_name_egg[name][0] - else: - return None + if name in _cache_name: + return _cache_name[name][0] + elif use_egg_info and name in _cache_name_egg: + return _cache_name_egg[name][0] + else: + return None def obsoletes_distribution(name, version=None, use_egg_info=False): -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:45 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:45 +0200 Subject: [Python-checkins] distutils2: Fixed the test suite to accomodate the new changes Message-ID: tarek.ziade pushed 59a04405c5af to distutils2: http://hg.python.org/distutils2/rev/59a04405c5af changeset: 430:59a04405c5af user: Josip Djolonga date: Sun Jul 18 03:34:33 2010 +0200 summary: Fixed the test suite to accomodate the new changes files: src/distutils2/_backport/tests/fake_dists/truffles-5.0.egg-info, src/distutils2/_backport/tests/test_pkgutil.py, src/distutils2/tests/test_install.py diff --git a/src/distutils2/_backport/tests/fake_dists/truffles-5.0.egg-info b/src/distutils2/_backport/tests/fake_dists/truffles-5.0.egg-info new file mode 100644 --- /dev/null +++ b/src/distutils2/_backport/tests/fake_dists/truffles-5.0.egg-info @@ -0,0 +1,3 @@ +Metadata-Version: 1.2 +Name: truffles +Version: 5.0 diff --git a/src/distutils2/_backport/tests/test_pkgutil.py b/src/distutils2/_backport/tests/test_pkgutil.py --- a/src/distutils2/_backport/tests/test_pkgutil.py +++ b/src/distutils2/_backport/tests/test_pkgutil.py @@ -485,7 +485,7 @@ l = [dist.name for dist in provides_distribution('truffles', '>1.5', use_egg_info=True)] - checkLists(l, ['bacon']) + checkLists(l, ['bacon', 'truffles']) l = [dist.name for dist in provides_distribution('truffles', '>=1.0')] checkLists(l, ['choxie', 'towel-stuff']) diff --git a/src/distutils2/tests/test_install.py b/src/distutils2/tests/test_install.py --- a/src/distutils2/tests/test_install.py +++ b/src/distutils2/tests/test_install.py @@ -195,11 +195,12 @@ cmd.ensure_finalized() cmd.run() - # let's check the RECORD file was created with one - # line (the egg info file) + # let's check the RECORD file was created with four + # lines, one for each .dist-info entry: METADATA, + # INSTALLER, REQUSTED, RECORD f = open(cmd.record) try: - self.assertEqual(len(f.readlines()), 1) + self.assertEqual(len(f.readlines()), 4) finally: f.close() -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:46 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:46 +0200 Subject: [Python-checkins] distutils2: Merged with tarek Message-ID: tarek.ziade pushed dd1db781cb58 to distutils2: http://hg.python.org/distutils2/rev/dd1db781cb58 changeset: 431:dd1db781cb58 parent: 430:59a04405c5af parent: 382:733c3bdaa25e user: Josip Djolonga date: Sun Jul 18 04:13:53 2010 +0200 summary: Merged with tarek files: diff --git a/src/distutils2/command/bdist_dumb.py b/src/distutils2/command/bdist_dumb.py --- a/src/distutils2/command/bdist_dumb.py +++ b/src/distutils2/command/bdist_dumb.py @@ -115,8 +115,9 @@ % (repr(install.install_base), repr(install.install_platbase))) else: - archive_root = os.path.join(self.bdist_dir, - ensure_relative(install.install_base)) + archive_root = os.path.join( + self.bdist_dir, + self._ensure_relative(install.install_base)) # Make the archive filename = self.make_archive(pseudoinstall_root, @@ -135,3 +136,9 @@ else: rmtree(self.bdist_dir) + def _ensure_relative(self, path): + # copied from dir_util, deleted + drive, path = os.path.splitdrive(path) + if path[0:1] == os.sep: + path = drive + path[1:] + return path diff --git a/src/distutils2/command/bdist_wininst.py b/src/distutils2/command/bdist_wininst.py --- a/src/distutils2/command/bdist_wininst.py +++ b/src/distutils2/command/bdist_wininst.py @@ -8,6 +8,7 @@ import sys import os import string +from shutil import rmtree try: from sysconfig import get_python_version except ImportError: diff --git a/src/distutils2/command/build_py.py b/src/distutils2/command/build_py.py --- a/src/distutils2/command/build_py.py +++ b/src/distutils2/command/build_py.py @@ -9,6 +9,7 @@ import logging from glob import glob +import distutils2 from distutils2.core import Command from distutils2.errors import DistutilsOptionError, DistutilsFileError from distutils2.util import convert_path diff --git a/src/distutils2/command/cmd.py b/src/distutils2/command/cmd.py --- a/src/distutils2/command/cmd.py +++ b/src/distutils2/command/cmd.py @@ -6,7 +6,7 @@ __revision__ = "$Id: cmd.py 75192 2009-10-02 23:49:48Z tarek.ziade $" -import sys, os, re +import os, re from distutils2.errors import DistutilsOptionError from distutils2 import util from distutils2 import log @@ -447,7 +447,7 @@ # If 'outfile' must be regenerated (either because it doesn't # exist, is out-of-date, or the 'force' flag is true) then # perform the action that presumably regenerates it - if self.force or dep_util.newer_group(infiles, outfile): + if self.force or util.newer_group(infiles, outfile): self.execute(func, args, exec_msg, level) # Otherwise, print the "skip" message diff --git a/src/distutils2/command/sdist.py b/src/distutils2/command/sdist.py --- a/src/distutils2/command/sdist.py +++ b/src/distutils2/command/sdist.py @@ -18,12 +18,11 @@ from distutils2._backport.shutil import get_archive_formats from distutils2.core import Command -from distutils2 import util from distutils2.errors import (DistutilsPlatformError, DistutilsOptionError, DistutilsTemplateError) from distutils2.manifest import Manifest from distutils2 import log -from distutils2.util import convert_path, newer +from distutils2.util import convert_path def show_formats(): """Print all possible values for the 'formats' option (used by diff --git a/src/distutils2/command/upload_docs.py b/src/distutils2/command/upload_docs.py --- a/src/distutils2/command/upload_docs.py +++ b/src/distutils2/command/upload_docs.py @@ -1,4 +1,4 @@ -import base64, httplib, os.path, socket, tempfile, urlparse, zipfile +import base64, httplib, os.path, socket, urlparse, zipfile from cStringIO import StringIO from distutils2 import log from distutils2.command.upload import upload @@ -81,7 +81,6 @@ raise DistutilsFileError(mesg % upload_dir) def run(self): - tmp_dir = tempfile.mkdtemp() name = self.distribution.metadata['Name'] zip_file = zip_dir(self.upload_dir) @@ -124,7 +123,7 @@ elif r.status == 301: location = r.getheader('Location') if location is None: - location = 'http://packages.python.org/%s/' % meta.get_name() + location = 'http://packages.python.org/%s/' % name self.announce('Upload successful. Visit %s' % location, log.INFO) else: diff --git a/src/distutils2/extension.py b/src/distutils2/extension.py --- a/src/distutils2/extension.py +++ b/src/distutils2/extension.py @@ -5,14 +5,8 @@ __revision__ = "$Id: extension.py 77704 2010-01-23 09:23:15Z tarek.ziade $" -import os import warnings -try: - import sysconfig -except ImportError: - from distutils2._backport import sysconfig - # This class is really only used by the "build_ext" command, so it might # make sense to put it in distutils.command.build_ext. However, that # module is already big enough, and I want to make this class a bit more diff --git a/src/distutils2/tests/test_build_ext.py b/src/distutils2/tests/test_build_ext.py --- a/src/distutils2/tests/test_build_ext.py +++ b/src/distutils2/tests/test_build_ext.py @@ -368,4 +368,4 @@ else: return unittest.makeSuite(BuildExtTestCase) if __name__ == '__main__': - distsutils2.tests.run_unittest(test_suite()) + distutils2.tests.run_unittest(test_suite()) diff --git a/src/distutils2/tests/test_depgraph.py b/src/distutils2/tests/test_depgraph.py --- a/src/distutils2/tests/test_depgraph.py +++ b/src/distutils2/tests/test_depgraph.py @@ -10,7 +10,7 @@ import re try: import cStringIO as StringIO -except ImportErorr: +except ImportError: import StringIO class DepGraphTestCase(support.LoggingSilencer, diff --git a/src/distutils2/tests/test_manifest.py b/src/distutils2/tests/test_manifest.py --- a/src/distutils2/tests/test_manifest.py +++ b/src/distutils2/tests/test_manifest.py @@ -3,6 +3,7 @@ import sys import logging +from distutils2.tests import run_unittest from distutils2.tests import support from distutils2.tests.support import unittest from distutils2.manifest import Manifest diff --git a/src/distutils2/tests/test_metadata.py b/src/distutils2/tests/test_metadata.py --- a/src/distutils2/tests/test_metadata.py +++ b/src/distutils2/tests/test_metadata.py @@ -5,6 +5,7 @@ from distutils2.metadata import (DistributionMetadata, _interpret, PKG_INFO_PREFERRED_VERSION) +from distutils2.tests import run_unittest from distutils2.tests.support import unittest, LoggingSilencer from distutils2.errors import (MetadataConflictError, MetadataUnrecognizedVersionError) diff --git a/src/distutils2/tests/test_pypi_versions.py b/src/distutils2/tests/test_pypi_versions.py --- a/src/distutils2/tests/test_pypi_versions.py +++ b/src/distutils2/tests/test_pypi_versions.py @@ -19,6 +19,7 @@ import os.path from distutils2.version import suggest_normalized_version +from distutils2.tests import run_unittest from distutils2.tests.support import unittest def test_pypi(): @@ -52,7 +53,7 @@ print "Saving package info..." f = open(INDEX_PICKLE_FILE, 'wb') try: - pickle.dump(package_info, o) + pickle.dump(package_info, f) finally: f.close() diff --git a/src/distutils2/version.py b/src/distutils2/version.py --- a/src/distutils2/version.py +++ b/src/distutils2/version.py @@ -1,4 +1,3 @@ -import sys import re from distutils2.errors import IrrationalVersionError, HugeMajorVersionNumError diff --git a/src/runtests-cov.py b/src/runtests-cov.py --- a/src/runtests-cov.py +++ b/src/runtests-cov.py @@ -23,16 +23,16 @@ return dirnames def parse_opts(): - parser = OptionParser(usage="%prog [OPTIONS]", + parser = OptionParser(usage="%prog [OPTIONS]", description="run the distutils2 unittests") - parser.add_option("-q", "--quiet", help="do not print verbose messages", + parser.add_option("-q", "--quiet", help="do not print verbose messages", action="store_true", default=False) parser.add_option("-c", "--coverage", action="store_true", default=False, help="produce a coverage report at the end of the run") parser.add_option("-r", "--report", action="store_true", default=False, help="produce a coverage report from the last test run") - parser.add_option("-m", "--show-missing", action="store_true", + parser.add_option("-m", "--show-missing", action="store_true", default=False, help=("Show line numbers of statements in each module " "that weren't executed.")) @@ -42,12 +42,12 @@ def coverage_report(opts): import coverage - import unittest2 + from distutils2.tests.support import unittest cov = coverage.coverage() cov.load() prefixes = ["runtests", "distutils2/tests", "distutils2/_backport"] - prefixes += ignore_prefixes(unittest2) + prefixes += ignore_prefixes(unittest) try: import docutils @@ -70,10 +70,10 @@ opts, args = parse_opts() verbose = not opts.quiet ret = 0 - + if opts.coverage or opts.report: import coverage - + if opts.coverage: cov = coverage.coverage() cov.erase() @@ -86,9 +86,9 @@ if opts.report or opts.coverage: coverage_report(opts) - + return ret - + def run_tests(verbose): import distutils2.tests from distutils2.tests import run_unittest, reap_children, TestFailed diff --git a/src/setup.py b/src/setup.py --- a/src/setup.py +++ b/src/setup.py @@ -11,7 +11,6 @@ from distutils2.compiler.ccompiler import new_compiler from distutils2.command.sdist import sdist from distutils2.command.install import install -from distutils2 import __version__ as VERSION from distutils2.util import find_packages f = open('README.txt') -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:46 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:46 +0200 Subject: [Python-checkins] distutils2: Add renaming ideas after IRC discussion, fix typo Message-ID: tarek.ziade pushed 0a8911f43e3e to distutils2: http://hg.python.org/distutils2/rev/0a8911f43e3e changeset: 434:0a8911f43e3e parent: 422:d6315cf22846 user: ?ric Araujo date: Tue Aug 03 17:27:39 2010 +0200 summary: Add renaming ideas after IRC discussion, fix typo files: src/DEVNOTES.txt diff --git a/src/DEVNOTES.txt b/src/DEVNOTES.txt --- a/src/DEVNOTES.txt +++ b/src/DEVNOTES.txt @@ -9,7 +9,7 @@ that you have all Python versions installed from 2.4 to 2.7. - With Python 2.4, if you want to run tests with runtests.py, or run - ust one test directly, be sure to run python2.4 setup.py build_ext + just one test directly, be sure to run python2.4 setup.py build_ext first, else tests won't find _hashlib or _md5. When using tests.sh, build_ext is automatically done. @@ -18,3 +18,5 @@ - DistributionMetadata > Metadata or ReleaseMetadata - pkgutil > pkgutil.__init__ + new pkgutil.database (or better name) - pypi > index + - RationalizedVersion > Version + - suggest_rationalized_version > suggest -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:46 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:46 +0200 Subject: [Python-checkins] distutils2: Some fixes after Eric's suggestions Message-ID: tarek.ziade pushed 6bdf9e1e1b16 to distutils2: http://hg.python.org/distutils2/rev/6bdf9e1e1b16 changeset: 432:6bdf9e1e1b16 user: Josip Djolonga date: Mon Jul 19 02:22:06 2010 +0200 summary: Some fixes after Eric's suggestions files: docs/source/pkgutil.rst, src/distutils2/_backport/pkgutil.py, src/distutils2/command/install.py diff --git a/docs/source/pkgutil.rst b/docs/source/pkgutil.rst --- a/docs/source/pkgutil.rst +++ b/docs/source/pkgutil.rst @@ -23,7 +23,8 @@ For performance purposes, the list of distributions is being internally cached. It is enabled by default, but you can turn it off or clear it using -:func:`distutils2._backport.pkgutil.set_cache_enabled` and +:func:`distutils2._backport.pkgutil.enable_cache`, +:func:`distutils2._backport.pkgutil.disable_cache` and :func:`distutils2._backport.pkgutil.clear_cache`. @@ -58,7 +59,7 @@ print('=====') for (path, md5, size) in dist.get_installed_files(): print('* Path: %s' % path) - print(' Hash %s, Size: %s bytes' % (md5, size)) + print(' Hash %s, Size: %s bytes' % (md5, size)) print('Metadata') print('========') for key, value in dist.metadata.items(): diff --git a/src/distutils2/_backport/pkgutil.py b/src/distutils2/_backport/pkgutil.py --- a/src/distutils2/_backport/pkgutil.py +++ b/src/distutils2/_backport/pkgutil.py @@ -27,8 +27,8 @@ 'ImpImporter', 'ImpLoader', 'read_code', 'extend_path', 'Distribution', 'EggInfoDistribution', 'distinfo_dirname', 'get_distributions', 'get_distribution', 'get_file_users', - 'provides_distribution', 'obsoletes_distribution', 'set_cache_enabled', - 'clear_cache' + 'provides_distribution', 'obsoletes_distribution', + 'enable_cache', 'disable_cache', 'clear_cache' ] @@ -626,20 +626,27 @@ _cache_enabled = True -def set_cache_enabled(flag): +def enable_cache(): """ - Enables or disables the internal cache depending on *flag*. + Enables the internal cache. Note that this function will not clear the cache in any case, for that functionality see :func:`clear_cache`. - - :parameter flag: - :type flag: boolean """ global _cache_enabled - _cache_enabled = flag + _cache_enabled = True +def disable_cache(): + """ + Disables the internal cache. + + Note that this function will not clear the cache in any case, for that + functionality see :func:`clear_cache`. + """ + global _cache_enabled + + _cache_enabled = False def clear_cache(): """ Clears the internal cache. """ @@ -654,12 +661,12 @@ _cache_generated_egg = False -def _yield_distributions(dist, egg): +def _yield_distributions(include_dist, include_egg): """ Yield .dist-info and .egg(-info) distributions, based on the arguments - :parameter dist: yield .dist-info distributions - :parameter egg: yield .egg(-info) distributions + :parameter include_dist: yield .dist-info distributions + :parameter include_egg: yield .egg(-info) distributions """ for path in sys.path: realpath = os.path.realpath(path) @@ -667,10 +674,10 @@ continue for dir in os.listdir(realpath): dist_path = os.path.join(realpath, dir) - if dist and dir.endswith('.dist-info'): + if include_dist and dir.endswith('.dist-info'): yield Distribution(dist_path) - elif egg and (dir.endswith('.egg-info') or - dir.endswith('.egg')): + elif include_egg and (dir.endswith('.egg-info') or + dir.endswith('.egg')): yield EggInfoDistribution(dist_path) @@ -716,7 +723,7 @@ requested = False """A boolean that indicates whether the ``REQUESTED`` metadata file is present (in other words, whether the package was installed by user - request).""" + request or it was installed as a dependency).""" def __init__(self, path): if _cache_enabled and path in _cache_path: diff --git a/src/distutils2/command/install.py b/src/distutils2/command/install.py --- a/src/distutils2/command/install.py +++ b/src/distutils2/command/install.py @@ -79,7 +79,7 @@ ('record=', None, "filename in which to record list of installed files"), - + # .dist-info related arguments, read by install_dist_info ('no-distinfo', None, 'do not create a .dist-info directory'), ('distinfo-dir=', None, @@ -170,7 +170,7 @@ #self.install_info = None self.record = None - + # .dist-info related options self.no_distinfo = None self.distinfo_dir = None @@ -323,7 +323,7 @@ # Punt on doc directories for now -- after all, we're punting on # documentation completely! - + if self.no_distinfo is None: self.no_distinfo = False -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:46 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:46 +0200 Subject: [Python-checkins] distutils2: added upload_docs option to upload command Message-ID: tarek.ziade pushed 853e5b093ab8 to distutils2: http://hg.python.org/distutils2/rev/853e5b093ab8 changeset: 437:853e5b093ab8 parent: 382:733c3bdaa25e user: Konrad Delong date: Thu Jul 22 19:31:26 2010 +0200 summary: added upload_docs option to upload command files: docs/source/commands.rst, docs/source/index.rst, docs/source/new_commands.rst, src/distutils2/command/upload.py, src/distutils2/tests/test_upload.py diff --git a/docs/source/new_commands.rst b/docs/source/commands.rst rename from docs/source/new_commands.rst rename to docs/source/commands.rst --- a/docs/source/new_commands.rst +++ b/docs/source/commands.rst @@ -6,6 +6,50 @@ You might recognize some of them from other projects, like Distribute or Setuptools. +``upload`` - Upload source and/or binary distributions to PyPI +============================================================== + +The Python Package Index (PyPI) not only stores the package info, but also the +package data if the author of the package wishes to. The distutils command +:command:`upload` pushes the distribution files to PyPI. + +The command is invoked immediately after building one or more distribution +files. For example, the command :: + + python setup.py sdist bdist_wininst upload + +will cause the source distribution and the Windows installer to be uploaded to +PyPI. Note that these will be uploaded even if they are built using an earlier +invocation of :file:`setup.py`, but that only distributions named on the command +line for the invocation including the :command:`upload` command are uploaded. + +The :command:`upload` command uses the username, password, and repository URL +from the :file:`$HOME/.pypirc` file . If a :command:`register` command was +previously called in the same command, and if the password was entered in the +prompt, :command:`upload` will reuse the entered password. This is useful if +you do not want to store a clear text password in the :file:`$HOME/.pypirc` +file. + +The ``upload`` command has a few options worth noting: + +``--sign, -s`` + Sign each uploaded file using GPG (GNU Privacy Guard). The ``gpg`` program + must be available for execution on the system ``PATH``. + +``--identity=NAME, -i NAME`` + Specify the identity or key name for GPG to use when signing. The value of + this option will be passed through the ``--local-user`` option of the + ``gpg`` program. + +``--show-response`` + Display the full response text from server; this is useful for debugging + PyPI problems. + +``--repository=URL, -r URL`` + The URL of the repository to upload to. Defaults to + http://pypi.python.org/pypi (i.e., the main PyPI installation). + + ``upload_docs`` - Upload package documentation to PyPI ====================================================== diff --git a/docs/source/index.rst b/docs/source/index.rst --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -14,7 +14,7 @@ metadata pkgutil depgraph - new_commands + commands test_framework pypi version diff --git a/src/distutils2/command/upload.py b/src/distutils2/command/upload.py --- a/src/distutils2/command/upload.py +++ b/src/distutils2/command/upload.py @@ -26,6 +26,7 @@ ('sign', 's', 'sign files to upload using gpg'), ('identity=', 'i', 'GPG identity used to sign files'), + ('upload-docs', None, 'Run upload_docs as well'), ] boolean_options = PyPIRCCommand.boolean_options + ['sign'] @@ -37,6 +38,7 @@ self.show_response = 0 self.sign = False self.identity = None + self.upload_docs = False def finalize_options(self): PyPIRCCommand.finalize_options(self) @@ -61,6 +63,12 @@ raise DistutilsOptionError("No dist file created in earlier command") for command, pyversion, filename in self.distribution.dist_files: self.upload_file(command, pyversion, filename) + if self.upload_docs: + upload_docs = self.get_finalized_command("upload_docs") + upload_docs.repository = self.repository + upload_docs.username = self.username + upload_docs.password = self.password + upload_docs.run() # XXX to be refactored with register.post_to_server def upload_file(self, command, pyversion, filename): diff --git a/src/distutils2/tests/test_upload.py b/src/distutils2/tests/test_upload.py --- a/src/distutils2/tests/test_upload.py +++ b/src/distutils2/tests/test_upload.py @@ -76,6 +76,38 @@ self.assertEqual(handler.command, 'POST') self.assertNotIn('\n', headers['authorization']) + def test_upload_docs(self): + path = os.path.join(self.tmp_dir, 'xxx') + self.write_file(path) + command, pyversion, filename = 'xxx', '2.6', path + dist_files = [(command, pyversion, filename)] + docs_path = os.path.join(self.tmp_dir, "build", "docs") + os.makedirs(docs_path) + self.write_file(os.path.join(docs_path, "index.html"), "yellow") + self.write_file(self.rc, PYPIRC) + + # lets run it + pkg_dir, dist = self.create_dist(dist_files=dist_files, author=u'd??d??') + + cmd = upload(dist) + cmd.get_finalized_command("build").run() + cmd.upload_docs = True + cmd.ensure_finalized() + cmd.repository = self.pypi.full_address + try: + prev_dir = os.getcwd() + os.chdir(self.tmp_dir) + cmd.run() + finally: + os.chdir(prev_dir) + + handler, request_data = self.pypi.requests[-1] + action, name, content =\ + request_data.split("----------------GHSKFJDLGDS7543FJKLFHRE75642756743254")[1:4] + + self.assertIn('name=":action"', action) + self.assertIn("doc_upload", action) + def test_suite(): return unittest.makeSuite(UploadTestCase) -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:46 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:46 +0200 Subject: [Python-checkins] distutils2: Fix merge after running tests Message-ID: tarek.ziade pushed 70cc02f1c720 to distutils2: http://hg.python.org/distutils2/rev/70cc02f1c720 changeset: 436:70cc02f1c720 user: ?ric Araujo date: Tue Aug 03 22:50:41 2010 +0200 summary: Fix merge after running tests files: src/distutils2/command/install_distinfo.py, src/distutils2/tests/test_install_distinfo.py, src/tests.sh diff --git a/src/distutils2/command/install_distinfo.py b/src/distutils2/command/install_distinfo.py --- a/src/distutils2/command/install_distinfo.py +++ b/src/distutils2/command/install_distinfo.py @@ -10,15 +10,16 @@ automatically by the ``install`` command. """ +import os +import csv +import re from distutils2.command.cmd import Command from distutils2 import log from distutils2._backport.shutil import rmtree - - -import csv -import hashlib -import os -import re +try: + import hashlib +except ImportError: + from distutils2._backport import hashlib class install_distinfo(Command): diff --git a/src/distutils2/tests/test_install_distinfo.py b/src/distutils2/tests/test_install_distinfo.py --- a/src/distutils2/tests/test_install_distinfo.py +++ b/src/distutils2/tests/test_install_distinfo.py @@ -4,13 +4,17 @@ import sys import csv -from distutils2._backport import hashlib from distutils2.command.install_distinfo import install_distinfo from distutils2.core import Command from distutils2.metadata import DistributionMetadata from distutils2.tests import support from distutils2.tests.support import unittest +try: + import hashlib +except ImportError: + from distutils2._backport import hashlib + class DummyInstallCmd(Command): @@ -143,9 +147,9 @@ dirs = [] for dir in os.listdir(fake_dists): full_path = os.path.join(fake_dists, dir) - if not dir.endswith(('.egg', '.egg-info', '.dist-info')) \ - and os.path.isdir(full_path): - dirs.append(full_path) + if (not dir.endswith('.egg') or dir.endswith('.egg-info') or + dir.endswith('.dist-info')) and os.path.isdir(full_path): + dirs.append(full_path) for dir in dirs: for (path, subdirs, files) in os.walk(dir): diff --git a/src/tests.sh b/src/tests.sh --- a/src/tests.sh +++ b/src/tests.sh @@ -1,7 +1,7 @@ #!/bin/sh echo -n "Running tests for Python 2.4... " rm -f distutils2/_backport/_hashlib.so -python2.4 setup.py build_ext -q 2> /dev/null > /dev/null +python2.4 setup.py build_ext -f -q 2> /dev/null > /dev/null python2.4 -Wd runtests.py -q 2> /dev/null if [ $? -ne 0 ];then echo "Failed" -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:46 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:46 +0200 Subject: [Python-checkins] distutils2: Merge install_distinfo command Message-ID: tarek.ziade pushed ec1ac1605249 to distutils2: http://hg.python.org/distutils2/rev/ec1ac1605249 changeset: 435:ec1ac1605249 parent: 434:0a8911f43e3e parent: 433:aaf2faeb75e5 user: ?ric Araujo date: Tue Aug 03 17:29:40 2010 +0200 summary: Merge install_distinfo command files: src/distutils2/_backport/tests/test_pkgutil.py, src/distutils2/command/install_egg_info.py, src/distutils2/tests/support.py, src/distutils2/tests/test_install_distinfo.py diff --git a/docs/source/pkgutil.rst b/docs/source/pkgutil.rst --- a/docs/source/pkgutil.rst +++ b/docs/source/pkgutil.rst @@ -17,6 +17,17 @@ first a complete documentation of the functions and classes is provided and then several use cases are presented. +Caching ++++++++ + +For performance purposes, the list of distributions is being internally +cached. It is enabled by default, but you can turn it off or clear +it using +:func:`distutils2._backport.pkgutil.enable_cache`, +:func:`distutils2._backport.pkgutil.disable_cache` and +:func:`distutils2._backport.pkgutil.clear_cache`. + + API Reference ============= @@ -48,7 +59,7 @@ print('=====') for (path, md5, size) in dist.get_installed_files(): print('* Path: %s' % path) - print(' Hash %s, Size: %s bytes' % (md5, size)) + print(' Hash %s, Size: %s bytes' % (md5, size)) print('Metadata') print('========') for key, value in dist.metadata.items(): diff --git a/src/distutils2/_backport/pkgutil.py b/src/distutils2/_backport/pkgutil.py --- a/src/distutils2/_backport/pkgutil.py +++ b/src/distutils2/_backport/pkgutil.py @@ -20,6 +20,7 @@ import re import warnings + __all__ = [ 'get_importer', 'iter_importers', 'get_loader', 'find_loader', 'walk_packages', 'iter_modules', 'get_data', @@ -27,6 +28,7 @@ 'Distribution', 'EggInfoDistribution', 'distinfo_dirname', 'get_distributions', 'get_distribution', 'get_file_users', 'provides_distribution', 'obsoletes_distribution', + 'enable_cache', 'disable_cache', 'clear_cache' ] @@ -187,8 +189,8 @@ searches the current ``sys.path``, plus any modules that are frozen or built-in. - Note that :class:`ImpImporter` does not currently support being used by placement - on ``sys.meta_path``. + Note that :class:`ImpImporter` does not currently support being used by + placement on ``sys.meta_path``. """ def __init__(self, path=None): @@ -577,7 +579,8 @@ argument should be the name of a package, in standard module format (``foo.bar``). The resource argument should be in the form of a relative filename, using ``'/'`` as the path separator. The parent directory name - ``'..'`` is not allowed, and nor is a rooted name (starting with a ``'/'``). + ``'..'`` is not allowed, and nor is a rooted name (starting with a + ``'/'``). The function returns a binary string, which is the contents of the specified resource. @@ -613,6 +616,97 @@ DIST_FILES = ('INSTALLER', 'METADATA', 'RECORD', 'REQUESTED',) +# Cache +_cache_name = {} # maps names to Distribution instances +_cache_name_egg = {} # maps names to EggInfoDistribution instances +_cache_path = {} # maps paths to Distribution instances +_cache_path_egg = {} # maps paths to EggInfoDistribution instances +_cache_generated = False # indicates if .dist-info distributions are cached +_cache_generated_egg = False # indicates if .dist-info and .egg are cached +_cache_enabled = True + + +def enable_cache(): + """ + Enables the internal cache. + + Note that this function will not clear the cache in any case, for that + functionality see :func:`clear_cache`. + """ + global _cache_enabled + + _cache_enabled = True + +def disable_cache(): + """ + Disables the internal cache. + + Note that this function will not clear the cache in any case, for that + functionality see :func:`clear_cache`. + """ + global _cache_enabled + + _cache_enabled = False + +def clear_cache(): + """ Clears the internal cache. """ + global _cache_name, _cache_name_egg, cache_path, _cache_path_egg, \ + _cache_generated, _cache_generated_egg + + _cache_name = {} + _cache_name_egg = {} + _cache_path = {} + _cache_path_egg = {} + _cache_generated = False + _cache_generated_egg = False + + +def _yield_distributions(include_dist, include_egg): + """ + Yield .dist-info and .egg(-info) distributions, based on the arguments + + :parameter include_dist: yield .dist-info distributions + :parameter include_egg: yield .egg(-info) distributions + """ + for path in sys.path: + realpath = os.path.realpath(path) + if not os.path.isdir(realpath): + continue + for dir in os.listdir(realpath): + dist_path = os.path.join(realpath, dir) + if include_dist and dir.endswith('.dist-info'): + yield Distribution(dist_path) + elif include_egg and (dir.endswith('.egg-info') or + dir.endswith('.egg')): + yield EggInfoDistribution(dist_path) + + +def _generate_cache(use_egg_info=False): + global _cache_generated, _cache_generated_egg + + if _cache_generated_egg or (_cache_generated and not use_egg_info): + return + else: + gen_dist = not _cache_generated + gen_egg = use_egg_info + + for dist in _yield_distributions(gen_dist, gen_egg): + if isinstance(dist, Distribution): + _cache_path[dist.path] = dist + if not dist.name in _cache_name: + _cache_name[dist.name] = [] + _cache_name[dist.name].append(dist) + else: + _cache_path_egg[dist.path] = dist + if not dist.name in _cache_name_egg: + _cache_name_egg[dist.name] = [] + _cache_name_egg[dist.name].append(dist) + + if gen_dist: + _cache_generated = True + if gen_egg: + _cache_generated_egg = True + class Distribution(object): """Created with the *path* of the ``.dist-info`` directory provided to the @@ -627,15 +721,23 @@ """A :class:`distutils2.metadata.DistributionMetadata` instance loaded with the distribution's ``METADATA`` file.""" requested = False - """A boolean that indicates whether the ``REQUESTED`` metadata file is present - (in other words, whether the package was installed by user request).""" + """A boolean that indicates whether the ``REQUESTED`` metadata file is + present (in other words, whether the package was installed by user + request or it was installed as a dependency).""" def __init__(self, path): + if _cache_enabled and path in _cache_path: + self.metadata = _cache_path[path].metadata + else: + metadata_path = os.path.join(path, 'METADATA') + self.metadata = DistributionMetadata(path=metadata_path) + self.path = path - metadata_path = os.path.join(path, 'METADATA') - self.metadata = DistributionMetadata(path=metadata_path) self.name = self.metadata['name'] + if _cache_enabled and not path in _cache_path: + _cache_path[path] = self + def _get_records(self, local=False): RECORD = os.path.join(self.path, 'RECORD') record_reader = csv_reader(open(RECORD, 'rb'), delimiter=',') @@ -756,6 +858,11 @@ def __init__(self, path): self.path = path + if _cache_enabled and path in _cache_path_egg: + self.metadata = _cache_path_egg[path].metadata + self.name = self.metadata['Name'] + return + # reused from Distribute's pkg_resources def yield_lines(strs): """Yield non-empty/non-comment lines of a ``basestring`` or sequence""" @@ -787,7 +894,7 @@ requires = zipf.get_data('EGG-INFO/requires.txt') except IOError: requires = None - self.name = self.metadata['name'] + self.name = self.metadata['Name'] elif path.endswith('.egg-info'): if os.path.isdir(path): path = os.path.join(path, 'PKG-INFO') @@ -840,6 +947,9 @@ else: self.metadata['Requires'] += reqs + if _cache_enabled: + _cache_path_egg[self.path] = self + def get_installed_files(self, local=False): return [] @@ -898,17 +1008,17 @@ :rtype: iterator of :class:`Distribution` and :class:`EggInfoDistribution` instances""" - for path in sys.path: - realpath = os.path.realpath(path) - if not os.path.isdir(realpath): - continue - for dir in os.listdir(realpath): - if dir.endswith('.dist-info'): - dist = Distribution(os.path.join(realpath, dir)) - yield dist - elif use_egg_info and (dir.endswith('.egg-info') or - dir.endswith('.egg')): - dist = EggInfoDistribution(os.path.join(realpath, dir)) + if not _cache_enabled: + for dist in _yield_distributions(True, use_egg_info): + yield dist + else: + _generate_cache(use_egg_info) + + for dist in _cache_path.itervalues(): + yield dist + + if use_egg_info: + for dist in _cache_path_egg.itervalues(): yield dist @@ -928,17 +1038,19 @@ value is expected. If the directory is not found, ``None`` is returned. :rtype: :class:`Distribution` or :class:`EggInfoDistribution` or None""" - found = None - for dist in get_distributions(): - if dist.name == name: - found = dist - break - if use_egg_info: - for dist in get_distributions(True): + if not _cache_enabled: + for dist in _yield_distributions(True, use_egg_info): if dist.name == name: - found = dist - break - return found + return dist + else: + _generate_cache(use_egg_info) + + if name in _cache_name: + return _cache_name[name][0] + elif use_egg_info and name in _cache_name_egg: + return _cache_name_egg[name][0] + else: + return None def obsoletes_distribution(name, version=None, use_egg_info=False): diff --git a/src/distutils2/_backport/tests/fake_dists/grammar-1.0a4.dist-info/METADATA b/src/distutils2/_backport/tests/fake_dists/grammar-1.0a4.dist-info/METADATA --- a/src/distutils2/_backport/tests/fake_dists/grammar-1.0a4.dist-info/METADATA +++ b/src/distutils2/_backport/tests/fake_dists/grammar-1.0a4.dist-info/METADATA @@ -2,3 +2,4 @@ Name: grammar Version: 1.0a4 Requires-Dist: truffles (>=1.2) +Author: Sherlock Holmes diff --git a/src/distutils2/_backport/tests/fake_dists/truffles-5.0.egg-info b/src/distutils2/_backport/tests/fake_dists/truffles-5.0.egg-info new file mode 100644 --- /dev/null +++ b/src/distutils2/_backport/tests/fake_dists/truffles-5.0.egg-info @@ -0,0 +1,3 @@ +Metadata-Version: 1.2 +Name: truffles +Version: 5.0 diff --git a/src/distutils2/_backport/tests/test_pkgutil.py b/src/distutils2/_backport/tests/test_pkgutil.py --- a/src/distutils2/_backport/tests/test_pkgutil.py +++ b/src/distutils2/_backport/tests/test_pkgutil.py @@ -25,12 +25,14 @@ except ImportError: from unittest2.compatibility import relpath +# Adapted from Python 2.7's trunk + # TODO Add a test for getting a distribution that is provided by another # distribution. # TODO Add a test for absolute pathed RECORD items (e.g. /etc/myapp/config.ini) -# Adapted from Python 2.7's trunk + class TestPkgUtilData(unittest.TestCase): def setUp(self): @@ -108,10 +110,14 @@ del sys.modules[pkg] + # Adapted from Python 2.7's trunk + + class TestPkgUtilPEP302(unittest.TestCase): class MyTestLoader(object): + def load_module(self, fullname): # Create an empty module mod = sys.modules.setdefault(fullname, imp.new_module(fullname)) @@ -120,13 +126,14 @@ # Make it a package mod.__path__ = [] # Count how many times the module is reloaded - mod.__dict__['loads'] = mod.__dict__.get('loads',0) + 1 + mod.__dict__['loads'] = mod.__dict__.get('loads', 0) + 1 return mod def get_data(self, path): return "Hello, world!" class MyTestImporter(object): + def find_module(self, fullname, path=None): return TestPkgUtilPEP302.MyTestLoader() @@ -319,7 +326,7 @@ current_path = os.path.abspath(os.path.dirname(__file__)) self.sys_path = sys.path[:] self.fake_dists_path = os.path.join(current_path, 'fake_dists') - sys.path[0:0] = [self.fake_dists_path] + sys.path.insert(0, self.fake_dists_path) def tearDown(self): sys.path[:] = self.sys_path @@ -366,8 +373,12 @@ if not isinstance(dist, Distribution): self.fail("item received was not a Distribution instance: " "%s" % type(dist)) - if dist.name in dict(fake_dists).keys(): + if dist.name in dict(fake_dists).keys() and \ + dist.path.startswith(self.fake_dists_path): found_dists.append((dist.name, dist.metadata['version'],)) + else: + # check that it doesn't find anything more than this + self.assertFalse(dist.path.startswith(self.fake_dists_path)) # otherwise we don't care what other distributions are found # Finally, test that we found all that we were looking for @@ -375,7 +386,8 @@ # Now, test if the egg-info distributions are found correctly as well fake_dists += [('bacon', '0.1'), ('cheese', '2.0.2'), - ('banana', '0.4'), ('strawberry', '0.6')] + ('banana', '0.4'), ('strawberry', '0.6'), + ('truffles', '5.0')] found_dists = [] dists = [dist for dist in get_distributions(use_egg_info=True)] @@ -384,8 +396,11 @@ isinstance(dist, EggInfoDistribution)): self.fail("item received was not a Distribution or " "EggInfoDistribution instance: %s" % type(dist)) - if dist.name in dict(fake_dists).keys(): + if dist.name in dict(fake_dists).keys() and \ + dist.path.startswith(self.fake_dists_path): found_dists.append((dist.name, dist.metadata['version'])) + else: + self.assertFalse(dist.path.startswith(self.fake_dists_path)) self.assertListEqual(sorted(fake_dists), sorted(found_dists)) @@ -485,7 +500,7 @@ l = [dist.name for dist in provides_distribution('truffles', '>1.5', use_egg_info=True)] - checkLists(l, ['bacon']) + checkLists(l, ['bacon', 'truffles']) l = [dist.name for dist in provides_distribution('truffles', '>=1.0')] checkLists(l, ['choxie', 'towel-stuff']) @@ -549,6 +564,33 @@ l = [dist.name for dist in obsoletes_distribution('truffles', '0.2')] checkLists(l, ['towel-stuff']) + def test_yield_distribution(self): + # tests the internal function _yield_distributions + from distutils2._backport.pkgutil import _yield_distributions + checkLists = lambda x, y: self.assertListEqual(sorted(x), sorted(y)) + + eggs = [('bacon', '0.1'), ('banana', '0.4'), ('strawberry', '0.6'), + ('truffles', '5.0'), ('cheese', '2.0.2')] + dists = [('choxie', '2.0.0.9'), ('grammar', '1.0a4'), + ('towel-stuff', '0.1')] + + checkLists([], _yield_distributions(False, False)) + + found = [(dist.name, dist.metadata['Version']) + for dist in _yield_distributions(False, True) + if dist.path.startswith(self.fake_dists_path)] + checkLists(eggs, found) + + found = [(dist.name, dist.metadata['Version']) + for dist in _yield_distributions(True, False) + if dist.path.startswith(self.fake_dists_path)] + checkLists(dists, found) + + found = [(dist.name, dist.metadata['Version']) + for dist in _yield_distributions(True, True) + if dist.path.startswith(self.fake_dists_path)] + checkLists(dists + eggs, found) + def test_suite(): suite = unittest.TestSuite() diff --git a/src/distutils2/command/install.py b/src/distutils2/command/install.py --- a/src/distutils2/command/install.py +++ b/src/distutils2/command/install.py @@ -79,15 +79,26 @@ ('record=', None, "filename in which to record list of installed files"), + + # .dist-info related arguments, read by install_dist_info + ('no-distinfo', None, 'do not create a .dist-info directory'), + ('distinfo-dir=', None, + 'directory where the the .dist-info directory will ' + 'be installed'), + ('installer=', None, 'the name of the installer'), + ('requested', None, 'generate a REQUESTED file'), + ('no-requested', None, 'do not generate a REQUESTED file'), + ('no-distinfo-record', None, 'do not generate a RECORD file'), ] - boolean_options = ['compile', 'force', 'skip-build'] + boolean_options = ['compile', 'force', 'skip-build', 'no-dist-info', + 'requested', 'no-dist-record',] user_options.append(('user', None, "install in user site-package '%s'" % \ get_path('purelib', '%s_user' % os.name))) boolean_options.append('user') - negative_opt = {'no-compile' : 'compile'} + negative_opt = {'no-compile' : 'compile', 'no-requested': 'requested'} def initialize_options(self): @@ -160,6 +171,13 @@ self.record = None + # .dist-info related options + self.no_distinfo = None + self.distinfo_dir = None + self.installer = None + self.requested = None + self.no_distinfo_record = None + # -- Option finalizing methods ------------------------------------- # (This is rather more involved than for most commands, @@ -306,6 +324,9 @@ # Punt on doc directories for now -- after all, we're punting on # documentation completely! + if self.no_distinfo is None: + self.no_distinfo = False + def dump_dirs(self, msg): """Dumps the list of user options.""" from distutils2.fancy_getopt import longopt_xlate @@ -586,5 +607,7 @@ ('install_headers', has_headers), ('install_scripts', has_scripts), ('install_data', has_data), - ('install_egg_info', lambda self:True), + # keep install_distinfo last, as it needs the record + # with files to be completely generated + ('install_distinfo', lambda self: not self.no_distinfo), ] diff --git a/src/distutils2/command/install_distinfo.py b/src/distutils2/command/install_distinfo.py new file mode 100644 --- /dev/null +++ b/src/distutils2/command/install_distinfo.py @@ -0,0 +1,174 @@ +""" +distutils.command.install_distinfo +================================== + +:Author: Josip Djolonga + +This module implements the ``install_distinfo`` command that creates the +``.dist-info`` directory for the distribution, as specified in :pep:`376`. +Usually, you do not have to call this command directly, it gets called +automatically by the ``install`` command. +""" + +from distutils2.command.cmd import Command +from distutils2 import log +from distutils2._backport.shutil import rmtree + + +import csv +import hashlib +import os +import re + + +class install_distinfo(Command): + """Install a .dist-info directory for the package""" + + description = 'Install a .dist-info directory for the package' + + user_options = [ + ('distinfo-dir=', None, + 'directory where the the .dist-info directory will ' + 'be installed'), + ('installer=', None, 'the name of the installer'), + ('requested', None, 'generate a REQUESTED file'), + ('no-requested', None, 'do not generate a REQUESTED file'), + ('no-distinfo-record', None, 'do not generate a RECORD file'), + ] + + boolean_options = [ + 'requested', + 'no-dist-record', + ] + + negative_opt = {'no-requested': 'requested'} + + def initialize_options(self): + self.distinfo_dir = None + self.installer = None + self.requested = None + self.no_distinfo_record = None + + def finalize_options(self): + self.set_undefined_options('install', + ('distinfo_dir', 'distinfo_dir'), + ('installer', 'installer'), + ('requested', 'requested'), + ('no_distinfo_record', + 'no_distinfo_record')) + + self.set_undefined_options('install_lib', + ('install_dir', 'distinfo_dir')) + + if self.installer is None: + self.installer = 'distutils' + if self.requested is None: + self.requested = True + if self.no_distinfo_record is None: + self.no_distinfo_record = False + + metadata = self.distribution.metadata + + basename = "%s-%s.dist-info" % ( + to_filename(safe_name(metadata['Name'])), + to_filename(safe_version(metadata['Version'])), + ) + + self.distinfo_dir = os.path.join(self.distinfo_dir, basename) + self.outputs = [] + + def run(self): + if not self.dry_run: + target = self.distinfo_dir + + if os.path.isdir(target) and not os.path.islink(target): + rmtree(target) + elif os.path.exists(target): + self.execute(os.unlink, (self.distinfo_dir,), + "Removing " + target) + + self.execute(os.makedirs, (target,), "Creating " + target) + + metadata_path = os.path.join(self.distinfo_dir, 'METADATA') + log.info('Creating %s' % (metadata_path,)) + self.distribution.metadata.write(metadata_path) + self.outputs.append(metadata_path) + + installer_path = os.path.join(self.distinfo_dir, 'INSTALLER') + log.info('Creating %s' % (installer_path,)) + f = open(installer_path, 'w') + try: + f.write(self.installer) + finally: + f.close() + self.outputs.append(installer_path) + + if self.requested: + requested_path = os.path.join(self.distinfo_dir, 'REQUESTED') + log.info('Creating %s' % (requested_path,)) + f = open(requested_path, 'w') + f.close() + self.outputs.append(requested_path) + + if not self.no_distinfo_record: + record_path = os.path.join(self.distinfo_dir, 'RECORD') + log.info('Creating %s' % (record_path,)) + f = open(record_path, 'wb') + try: + writer = csv.writer(f, delimiter=',', + lineterminator=os.linesep, + quotechar='"') + + install = self.get_finalized_command('install') + + for fpath in install.get_outputs(): + if fpath.endswith('.pyc') or fpath.endswith('.pyo'): + # do not put size and md5 hash, as in PEP-376 + writer.writerow((fpath, '', '')) + else: + size = os.path.getsize(fpath) + fd = open(fpath, 'r') + hash = hashlib.md5() + hash.update(fd.read()) + md5sum = hash.hexdigest() + writer.writerow((fpath, md5sum, size)) + + # add the RECORD file itself + writer.writerow((record_path, '', '')) + self.outputs.append(record_path) + finally: + f.close() + + def get_outputs(self): + return self.outputs + + +# The following routines are taken from setuptools' pkg_resources module and +# can be replaced by importing them from pkg_resources once it is included +# in the stdlib. + + +def safe_name(name): + """Convert an arbitrary string to a standard distribution name + + Any runs of non-alphanumeric/. characters are replaced with a single '-'. + """ + return re.sub('[^A-Za-z0-9.]+', '-', name) + + +def safe_version(version): + """Convert an arbitrary string to a standard version string + + Spaces become dots, and all other non-alphanumeric characters become + dashes, with runs of multiple dashes condensed to a single dash. + """ + version = version.replace(' ', '.') + return re.sub('[^A-Za-z0-9.]+', '-', version) + + +def to_filename(name): + """Convert a project or version name to its filename-escaped form + + Any '-' characters are currently replaced with '_'. + """ + return name.replace('-', '_') diff --git a/src/distutils2/command/install_egg_info.py b/src/distutils2/command/install_egg_info.py deleted file mode 100644 --- a/src/distutils2/command/install_egg_info.py +++ /dev/null @@ -1,83 +0,0 @@ -"""distutils.command.install_egg_info - -Implements the Distutils 'install_egg_info' command, for installing -a package's PKG-INFO metadata.""" - - -from distutils2.command.cmd import Command -from distutils2 import log -from distutils2._backport.shutil import rmtree -import os, sys, re - -class install_egg_info(Command): - """Install an .egg-info file for the package""" - - description = "Install package's PKG-INFO metadata as an .egg-info file" - user_options = [ - ('install-dir=', 'd', "directory to install to"), - ] - - def initialize_options(self): - self.install_dir = None - - def finalize_options(self): - metadata = self.distribution.metadata - self.set_undefined_options('install_lib',('install_dir','install_dir')) - basename = "%s-%s-py%s.egg-info" % ( - to_filename(safe_name(metadata['Name'])), - to_filename(safe_version(metadata['Version'])), - sys.version[:3] - ) - self.target = os.path.join(self.install_dir, basename) - self.outputs = [self.target] - - def run(self): - target = self.target - if os.path.isdir(target) and not os.path.islink(target): - if self.dry_run: - pass # XXX - else: - rmtree(target) - elif os.path.exists(target): - self.execute(os.unlink,(self.target,),"Removing "+target) - elif not os.path.isdir(self.install_dir): - self.execute(os.makedirs, (self.install_dir,), - "Creating "+self.install_dir) - log.info("Writing %s", target) - if not self.dry_run: - f = open(target, 'w') - self.distribution.metadata.write_file(f) - f.close() - - def get_outputs(self): - return self.outputs - - -# The following routines are taken from setuptools' pkg_resources module and -# can be replaced by importing them from pkg_resources once it is included -# in the stdlib. - -def safe_name(name): - """Convert an arbitrary string to a standard distribution name - - Any runs of non-alphanumeric/. characters are replaced with a single '-'. - """ - return re.sub('[^A-Za-z0-9.]+', '-', name) - - -def safe_version(version): - """Convert an arbitrary string to a standard version string - - Spaces become dots, and all other non-alphanumeric characters become - dashes, with runs of multiple dashes condensed to a single dash. - """ - version = version.replace(' ','.') - return re.sub('[^A-Za-z0-9.]+', '-', version) - - -def to_filename(name): - """Convert a project or version name to its filename-escaped form - - Any '-' characters are currently replaced with '_'. - """ - return name.replace('-','_') diff --git a/src/distutils2/tests/test_install.py b/src/distutils2/tests/test_install.py --- a/src/distutils2/tests/test_install.py +++ b/src/distutils2/tests/test_install.py @@ -195,11 +195,12 @@ cmd.ensure_finalized() cmd.run() - # let's check the RECORD file was created with one - # line (the egg info file) + # let's check the RECORD file was created with four + # lines, one for each .dist-info entry: METADATA, + # INSTALLER, REQUSTED, RECORD f = open(cmd.record) try: - self.assertEqual(len(f.readlines()), 1) + self.assertEqual(len(f.readlines()), 4) finally: f.close() diff --git a/src/distutils2/tests/test_install_distinfo.py b/src/distutils2/tests/test_install_distinfo.py new file mode 100644 --- /dev/null +++ b/src/distutils2/tests/test_install_distinfo.py @@ -0,0 +1,198 @@ +"""Tests for ``distutils2.command.install_distinfo``. """ + +import os +import sys +import csv + +from distutils2._backport import hashlib +from distutils2.command.install_distinfo import install_distinfo +from distutils2.core import Command +from distutils2.metadata import DistributionMetadata +from distutils2.tests import support +from distutils2.tests.support import unittest + + +class DummyInstallCmd(Command): + + def __init__(self, dist=None): + self.outputs = [] + self.distribution = dist + + def __getattr__(self, name): + return None + + def ensure_finalized(self): + pass + + def get_outputs(self): + return self.outputs + \ + self.get_finalized_command('install_distinfo').get_outputs() + + +class InstallDistinfoTestCase(support.TempdirManager, + support.LoggingSilencer, + support.EnvironGuard, + unittest.TestCase): + + checkLists = lambda self, x, y: self.assertListEqual(sorted(x), sorted(y)) + + def test_empty_install(self): + pkg_dir, dist = self.create_dist(name='foo', + version='1.0') + install_dir = self.mkdtemp() + + install = DummyInstallCmd(dist) + dist.command_obj['install'] = install + + cmd = install_distinfo(dist) + dist.command_obj['install_distinfo'] = cmd + + cmd.initialize_options() + cmd.distinfo_dir = install_dir + cmd.ensure_finalized() + cmd.run() + + self.checkLists(os.listdir(install_dir), ['foo-1.0.dist-info']) + + dist_info = os.path.join(install_dir, 'foo-1.0.dist-info') + self.checkLists(os.listdir(dist_info), + ['METADATA', 'RECORD', 'REQUESTED', 'INSTALLER']) + self.assertEqual(open(os.path.join(dist_info, 'INSTALLER')).read(), + 'distutils') + self.assertEqual(open(os.path.join(dist_info, 'REQUESTED')).read(), + '') + meta_path = os.path.join(dist_info, 'METADATA') + self.assertTrue(DistributionMetadata(path=meta_path).check()) + + def test_installer(self): + pkg_dir, dist = self.create_dist(name='foo', + version='1.0') + install_dir = self.mkdtemp() + + install = DummyInstallCmd(dist) + dist.command_obj['install'] = install + + cmd = install_distinfo(dist) + dist.command_obj['install_distinfo'] = cmd + + cmd.initialize_options() + cmd.distinfo_dir = install_dir + cmd.installer = 'bacon-python' + cmd.ensure_finalized() + cmd.run() + + dist_info = os.path.join(install_dir, 'foo-1.0.dist-info') + self.assertEqual(open(os.path.join(dist_info, 'INSTALLER')).read(), + 'bacon-python') + + def test_requested(self): + pkg_dir, dist = self.create_dist(name='foo', + version='1.0') + install_dir = self.mkdtemp() + + install = DummyInstallCmd(dist) + dist.command_obj['install'] = install + + cmd = install_distinfo(dist) + dist.command_obj['install_distinfo'] = cmd + + cmd.initialize_options() + cmd.distinfo_dir = install_dir + cmd.requested = False + cmd.ensure_finalized() + cmd.run() + + dist_info = os.path.join(install_dir, 'foo-1.0.dist-info') + self.checkLists(os.listdir(dist_info), + ['METADATA', 'RECORD', 'INSTALLER']) + + def test_no_record(self): + pkg_dir, dist = self.create_dist(name='foo', + version='1.0') + install_dir = self.mkdtemp() + + install = DummyInstallCmd(dist) + dist.command_obj['install'] = install + + cmd = install_distinfo(dist) + dist.command_obj['install_distinfo'] = cmd + + cmd.initialize_options() + cmd.distinfo_dir = install_dir + cmd.no_distinfo_record = True + cmd.ensure_finalized() + cmd.run() + + dist_info = os.path.join(install_dir, 'foo-1.0.dist-info') + self.checkLists(os.listdir(dist_info), + ['METADATA', 'REQUESTED', 'INSTALLER']) + + def test_record(self): + pkg_dir, dist = self.create_dist(name='foo', + version='1.0') + install_dir = self.mkdtemp() + + install = DummyInstallCmd(dist) + dist.command_obj['install'] = install + + fake_dists = os.path.join(os.path.dirname(__file__), '..', + '_backport', 'tests', 'fake_dists') + fake_dists = os.path.realpath(fake_dists) + + # for testing, we simply add all files from _backport's fake_dists + dirs = [] + for dir in os.listdir(fake_dists): + full_path = os.path.join(fake_dists, dir) + if not dir.endswith(('.egg', '.egg-info', '.dist-info')) \ + and os.path.isdir(full_path): + dirs.append(full_path) + + for dir in dirs: + for (path, subdirs, files) in os.walk(dir): + install.outputs += [os.path.join(path, f) for f in files] + install.outputs += [os.path.join('path', f + 'c') + for f in files if f.endswith('.py')] + + + cmd = install_distinfo(dist) + dist.command_obj['install_distinfo'] = cmd + + cmd.initialize_options() + cmd.distinfo_dir = install_dir + cmd.ensure_finalized() + cmd.run() + + dist_info = os.path.join(install_dir, 'foo-1.0.dist-info') + + expected = [] + for f in install.get_outputs(): + if f.endswith('.pyc') or \ + f == os.path.join(install_dir, 'foo-1.0.dist-info', 'RECORD'): + expected.append([f, '', '']) + else: + size = os.path.getsize(f) + md5 = hashlib.md5() + md5.update(open(f).read()) + hash = md5.hexdigest() + expected.append([f, hash, str(size)]) + + parsed = [] + f = open(os.path.join(dist_info, 'RECORD'), 'rb') + try: + reader = csv.reader(f, delimiter=',', + lineterminator=os.linesep, + quotechar='"') + parsed = list(reader) + finally: + f.close() + + self.maxDiff = None + self.checkLists(parsed, expected) + + +def test_suite(): + return unittest.makeSuite(InstallDistinfoTestCase) + + +if __name__ == "__main__": + unittest.main(defaultTest="test_suite") -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:46 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:46 +0200 Subject: [Python-checkins] distutils2: Added more tests Message-ID: tarek.ziade pushed aaf2faeb75e5 to distutils2: http://hg.python.org/distutils2/rev/aaf2faeb75e5 changeset: 433:aaf2faeb75e5 user: Josip Djolonga date: Thu Jul 22 16:20:49 2010 +0200 summary: Added more tests files: src/distutils2/_backport/pkgutil.py, src/distutils2/_backport/tests/test_pkgutil.py, src/distutils2/command/install_distinfo.py, src/distutils2/tests/support.py, src/distutils2/tests/test_install_distinfo.py diff --git a/src/distutils2/_backport/pkgutil.py b/src/distutils2/_backport/pkgutil.py --- a/src/distutils2/_backport/pkgutil.py +++ b/src/distutils2/_backport/pkgutil.py @@ -1009,7 +1009,7 @@ :rtype: iterator of :class:`Distribution` and :class:`EggInfoDistribution` instances""" if not _cache_enabled: - for dist in _yield_distributions(use_egg_info): + for dist in _yield_distributions(True, use_egg_info): yield dist else: _generate_cache(use_egg_info) @@ -1039,7 +1039,7 @@ :rtype: :class:`Distribution` or :class:`EggInfoDistribution` or None""" if not _cache_enabled: - for dist in _yield_distributions(use_egg_info): + for dist in _yield_distributions(True, use_egg_info): if dist.name == name: return dist else: diff --git a/src/distutils2/_backport/tests/test_pkgutil.py b/src/distutils2/_backport/tests/test_pkgutil.py --- a/src/distutils2/_backport/tests/test_pkgutil.py +++ b/src/distutils2/_backport/tests/test_pkgutil.py @@ -25,12 +25,14 @@ except ImportError: from unittest2.compatibility import relpath +# Adapted from Python 2.7's trunk + # TODO Add a test for getting a distribution that is provided by another # distribution. # TODO Add a test for absolute pathed RECORD items (e.g. /etc/myapp/config.ini) -# Adapted from Python 2.7's trunk + class TestPkgUtilData(unittest.TestCase): def setUp(self): @@ -108,10 +110,14 @@ del sys.modules[pkg] + # Adapted from Python 2.7's trunk + + class TestPkgUtilPEP302(unittest.TestCase): class MyTestLoader(object): + def load_module(self, fullname): # Create an empty module mod = sys.modules.setdefault(fullname, imp.new_module(fullname)) @@ -120,13 +126,14 @@ # Make it a package mod.__path__ = [] # Count how many times the module is reloaded - mod.__dict__['loads'] = mod.__dict__.get('loads',0) + 1 + mod.__dict__['loads'] = mod.__dict__.get('loads', 0) + 1 return mod def get_data(self, path): return "Hello, world!" class MyTestImporter(object): + def find_module(self, fullname, path=None): return TestPkgUtilPEP302.MyTestLoader() @@ -319,7 +326,7 @@ current_path = os.path.abspath(os.path.dirname(__file__)) self.sys_path = sys.path[:] self.fake_dists_path = os.path.join(current_path, 'fake_dists') - sys.path[0:0] = [self.fake_dists_path] + sys.path.insert(0, self.fake_dists_path) def tearDown(self): sys.path[:] = self.sys_path @@ -366,8 +373,12 @@ if not isinstance(dist, Distribution): self.fail("item received was not a Distribution instance: " "%s" % type(dist)) - if dist.name in dict(fake_dists).keys(): + if dist.name in dict(fake_dists).keys() and \ + dist.path.startswith(self.fake_dists_path): found_dists.append((dist.name, dist.metadata['version'],)) + else: + # check that it doesn't find anything more than this + self.assertFalse(dist.path.startswith(self.fake_dists_path)) # otherwise we don't care what other distributions are found # Finally, test that we found all that we were looking for @@ -375,7 +386,8 @@ # Now, test if the egg-info distributions are found correctly as well fake_dists += [('bacon', '0.1'), ('cheese', '2.0.2'), - ('banana', '0.4'), ('strawberry', '0.6')] + ('banana', '0.4'), ('strawberry', '0.6'), + ('truffles', '5.0')] found_dists = [] dists = [dist for dist in get_distributions(use_egg_info=True)] @@ -384,8 +396,11 @@ isinstance(dist, EggInfoDistribution)): self.fail("item received was not a Distribution or " "EggInfoDistribution instance: %s" % type(dist)) - if dist.name in dict(fake_dists).keys(): + if dist.name in dict(fake_dists).keys() and \ + dist.path.startswith(self.fake_dists_path): found_dists.append((dist.name, dist.metadata['version'])) + else: + self.assertFalse(dist.path.startswith(self.fake_dists_path)) self.assertListEqual(sorted(fake_dists), sorted(found_dists)) @@ -549,6 +564,33 @@ l = [dist.name for dist in obsoletes_distribution('truffles', '0.2')] checkLists(l, ['towel-stuff']) + def test_yield_distribution(self): + # tests the internal function _yield_distributions + from distutils2._backport.pkgutil import _yield_distributions + checkLists = lambda x, y: self.assertListEqual(sorted(x), sorted(y)) + + eggs = [('bacon', '0.1'), ('banana', '0.4'), ('strawberry', '0.6'), + ('truffles', '5.0'), ('cheese', '2.0.2')] + dists = [('choxie', '2.0.0.9'), ('grammar', '1.0a4'), + ('towel-stuff', '0.1')] + + checkLists([], _yield_distributions(False, False)) + + found = [(dist.name, dist.metadata['Version']) + for dist in _yield_distributions(False, True) + if dist.path.startswith(self.fake_dists_path)] + checkLists(eggs, found) + + found = [(dist.name, dist.metadata['Version']) + for dist in _yield_distributions(True, False) + if dist.path.startswith(self.fake_dists_path)] + checkLists(dists, found) + + found = [(dist.name, dist.metadata['Version']) + for dist in _yield_distributions(True, True) + if dist.path.startswith(self.fake_dists_path)] + checkLists(dists + eggs, found) + def test_suite(): suite = unittest.TestSuite() diff --git a/src/distutils2/command/install_distinfo.py b/src/distutils2/command/install_distinfo.py --- a/src/distutils2/command/install_distinfo.py +++ b/src/distutils2/command/install_distinfo.py @@ -27,13 +27,13 @@ description = 'Install a .dist-info directory for the package' user_options = [ - ('dist-info-dir=', None, + ('distinfo-dir=', None, 'directory where the the .dist-info directory will ' 'be installed'), ('installer=', None, 'the name of the installer'), ('requested', None, 'generate a REQUESTED file'), ('no-requested', None, 'do not generate a REQUESTED file'), - ('no-dist-record', None, 'do not generate a RECORD file'), + ('no-distinfo-record', None, 'do not generate a RECORD file'), ] boolean_options = [ diff --git a/src/distutils2/tests/support.py b/src/distutils2/tests/support.py --- a/src/distutils2/tests/support.py +++ b/src/distutils2/tests/support.py @@ -75,9 +75,9 @@ if os.path.exists(file_): os.remove(file_) - def mktempfile(self): + def mktempfile(self, *args, **kwargs): """Create a temporary file that will be cleaned up.""" - tempfile_ = tempfile.NamedTemporaryFile() + tempfile_ = tempfile.NamedTemporaryFile(*args, **kwargs) self.tempfiles.append(tempfile_.name) return tempfile_ diff --git a/src/distutils2/tests/test_install_distinfo.py b/src/distutils2/tests/test_install_distinfo.py new file mode 100644 --- /dev/null +++ b/src/distutils2/tests/test_install_distinfo.py @@ -0,0 +1,198 @@ +"""Tests for ``distutils2.command.install_distinfo``. """ + +import sys +import os +import hashlib +import csv + +from distutils2.command.install_distinfo import install_distinfo +from distutils2.tests import support +from distutils2.tests.support import unittest +from distutils2.core import Command +from distutils2.metadata import DistributionMetadata + + +class DummyInstallCmd(Command): + + def __init__(self, dist=None): + self.outputs = [] + self.distribution = dist + + def __getattr__(self, name): + return None + + def ensure_finalized(self): + pass + + def get_outputs(self): + return self.outputs + \ + self.get_finalized_command('install_distinfo').get_outputs() + + +class InstallDistinfoTestCase(support.TempdirManager, + support.LoggingSilencer, + support.EnvironGuard, + unittest.TestCase): + + checkLists = lambda self, x, y: self.assertListEqual(sorted(x), sorted(y)) + + def test_empty_install(self): + pkg_dir, dist = self.create_dist(name='foo', + version='1.0') + install_dir = self.mkdtemp() + + install = DummyInstallCmd(dist) + dist.command_obj['install'] = install + + cmd = install_distinfo(dist) + dist.command_obj['install_distinfo'] = cmd + + cmd.initialize_options() + cmd.distinfo_dir = install_dir + cmd.ensure_finalized() + cmd.run() + + self.checkLists(os.listdir(install_dir), ['foo-1.0.dist-info']) + + dist_info = os.path.join(install_dir, 'foo-1.0.dist-info') + self.checkLists(os.listdir(dist_info), + ['METADATA', 'RECORD', 'REQUESTED', 'INSTALLER']) + self.assertEqual(open(os.path.join(dist_info, 'INSTALLER')).read(), + 'distutils') + self.assertEqual(open(os.path.join(dist_info, 'REQUESTED')).read(), + '') + meta_path = os.path.join(dist_info, 'METADATA') + self.assertTrue(DistributionMetadata(path=meta_path).check()) + + def test_installer(self): + pkg_dir, dist = self.create_dist(name='foo', + version='1.0') + install_dir = self.mkdtemp() + + install = DummyInstallCmd(dist) + dist.command_obj['install'] = install + + cmd = install_distinfo(dist) + dist.command_obj['install_distinfo'] = cmd + + cmd.initialize_options() + cmd.distinfo_dir = install_dir + cmd.installer = 'bacon-python' + cmd.ensure_finalized() + cmd.run() + + dist_info = os.path.join(install_dir, 'foo-1.0.dist-info') + self.assertEqual(open(os.path.join(dist_info, 'INSTALLER')).read(), + 'bacon-python') + + def test_requested(self): + pkg_dir, dist = self.create_dist(name='foo', + version='1.0') + install_dir = self.mkdtemp() + + install = DummyInstallCmd(dist) + dist.command_obj['install'] = install + + cmd = install_distinfo(dist) + dist.command_obj['install_distinfo'] = cmd + + cmd.initialize_options() + cmd.distinfo_dir = install_dir + cmd.requested = False + cmd.ensure_finalized() + cmd.run() + + dist_info = os.path.join(install_dir, 'foo-1.0.dist-info') + self.checkLists(os.listdir(dist_info), + ['METADATA', 'RECORD', 'INSTALLER']) + + def test_no_record(self): + pkg_dir, dist = self.create_dist(name='foo', + version='1.0') + install_dir = self.mkdtemp() + + install = DummyInstallCmd(dist) + dist.command_obj['install'] = install + + cmd = install_distinfo(dist) + dist.command_obj['install_distinfo'] = cmd + + cmd.initialize_options() + cmd.distinfo_dir = install_dir + cmd.no_distinfo_record = True + cmd.ensure_finalized() + cmd.run() + + dist_info = os.path.join(install_dir, 'foo-1.0.dist-info') + self.checkLists(os.listdir(dist_info), + ['METADATA', 'REQUESTED', 'INSTALLER']) + + def test_record(self): + pkg_dir, dist = self.create_dist(name='foo', + version='1.0') + install_dir = self.mkdtemp() + + install = DummyInstallCmd(dist) + dist.command_obj['install'] = install + + fake_dists = os.path.join(os.path.dirname(__file__), '..', + '_backport', 'tests', 'fake_dists') + fake_dists = os.path.realpath(fake_dists) + + # for testing, we simply add all files from _backport's fake_dists + dirs = [] + for dir in os.listdir(fake_dists): + full_path = os.path.join(fake_dists, dir) + if not dir.endswith(('.egg', '.egg-info', '.dist-info')) \ + and os.path.isdir(full_path): + dirs.append(full_path) + + for dir in dirs: + for (path, subdirs, files) in os.walk(dir): + install.outputs += [os.path.join(path, f) for f in files] + install.outputs += [os.path.join('path', f + 'c') + for f in files if f.endswith('.py')] + + + cmd = install_distinfo(dist) + dist.command_obj['install_distinfo'] = cmd + + cmd.initialize_options() + cmd.distinfo_dir = install_dir + cmd.ensure_finalized() + cmd.run() + + dist_info = os.path.join(install_dir, 'foo-1.0.dist-info') + + expected = [] + for f in install.get_outputs(): + if f.endswith('.pyc') or \ + f == os.path.join(install_dir, 'foo-1.0.dist-info', 'RECORD'): + expected.append([f, '', '']) + else: + size = os.path.getsize(f) + md5 = hashlib.md5() + md5.update(open(f).read()) + hash = md5.hexdigest() + expected.append([f, hash, str(size)]) + + parsed = [] + f = open(os.path.join(dist_info, 'RECORD'), 'rb') + try: + reader = csv.reader(f, delimiter=',', + lineterminator=os.linesep, + quotechar='"') + parsed = list(reader) + finally: + f.close() + + self.maxDiff = None + self.checkLists(parsed, expected) + + +def test_suite(): + return unittest.makeSuite(InstallDistinfoTestCase) + + +if __name__ == "__main__": + unittest.main(defaultTest="test_suite") -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:46 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:46 +0200 Subject: [Python-checkins] distutils2: Use proper skips Message-ID: tarek.ziade pushed 21097737d121 to distutils2: http://hg.python.org/distutils2/rev/21097737d121 changeset: 440:21097737d121 user: ?ric Araujo date: Thu Aug 05 14:17:22 2010 +0200 summary: Use proper skips files: src/distutils2/tests/test_build_ext.py, src/distutils2/tests/test_install.py diff --git a/src/distutils2/tests/test_build_ext.py b/src/distutils2/tests/test_build_ext.py --- a/src/distutils2/tests/test_build_ext.py +++ b/src/distutils2/tests/test_build_ext.py @@ -126,11 +126,8 @@ # make sure we get some library dirs under solaris self.assertTrue(len(cmd.library_dirs) > 0) + @unittest.skipIf(sys.version < '2.6', 'requires Python 2.6 or higher') def test_user_site(self): - # site.USER_SITE was introduced in 2.6 - if sys.version < '2.6': - return - import site dist = Distribution({'name': 'xx'}) cmd = build_ext(dist) diff --git a/src/distutils2/tests/test_install.py b/src/distutils2/tests/test_install.py --- a/src/distutils2/tests/test_install.py +++ b/src/distutils2/tests/test_install.py @@ -75,11 +75,9 @@ check_path(cmd.install_scripts, os.path.join(destination, "bin")) check_path(cmd.install_data, destination) + @unittest.skipIf(sys.version < '2.6', 'requires Python 2.6 or higher') def test_user_site(self): - # site.USER_SITE was introduced in 2.6 - if sys.version < '2.6': - return - + # test install with --user # preparing the environment for the test self.old_user_base = get_config_var('userbase') self.old_user_site = get_path('purelib', '%s_user' % os.name) -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:46 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:46 +0200 Subject: [Python-checkins] distutils2: Add tests for link matchers Message-ID: tarek.ziade pushed bd63fd149823 to distutils2: http://hg.python.org/distutils2/rev/bd63fd149823 changeset: 444:bd63fd149823 parent: 367:70b98f177413 user: Alexis Metaireau date: Thu Jul 15 02:51:17 2010 +0200 summary: Add tests for link matchers files: src/distutils2/tests/test_pypi_simple.py diff --git a/src/distutils2/tests/test_pypi_simple.py b/src/distutils2/tests/test_pypi_simple.py --- a/src/distutils2/tests/test_pypi_simple.py +++ b/src/distutils2/tests/test_pypi_simple.py @@ -270,6 +270,29 @@ dists = index.find("foobar") self.assertEqual(4, len(dists)) + def test_get_link_matcher(self): + crawler = simple.SimpleIndex("http://example.org") + self.assertEqual('_simple_link_matcher', crawler._get_link_matcher( + "http://example.org/some/file").__name__) + self.assertEqual('_default_link_matcher', crawler._get_link_matcher( + "http://other-url").__name__) + + def test_default_link_matcher(self): + crawler = simple.SimpleIndex("http://example.org", mirrors=[]) + crawler.follow_externals = True + crawler._is_browsable = lambda *args:True + base_url = "http://example.org/some/file/" + content = """ +link +link2 +link2 + """ + found_links = dict(crawler._default_link_matcher(content, + base_url)).keys() + self.assertIn('http://example.org/some/homepage', found_links) + self.assertIn('http://example.org/some/simpleurl', found_links) + self.assertIn('http://example.org/some/download', found_links) + def test_suite(): return unittest.makeSuite(PyPISimpleTestCase) -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:46 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:46 +0200 Subject: [Python-checkins] distutils2: Make set_undefined_options more friendly Message-ID: tarek.ziade pushed 60931bb3e1d3 to distutils2: http://hg.python.org/distutils2/rev/60931bb3e1d3 changeset: 439:60931bb3e1d3 user: ?ric Araujo date: Wed Aug 04 18:12:31 2010 +0200 summary: Make set_undefined_options more friendly files: src/distutils2/command/bdist_dumb.py, src/distutils2/command/bdist_msi.py, src/distutils2/command/bdist_wininst.py, src/distutils2/command/build_clib.py, src/distutils2/command/build_ext.py, src/distutils2/command/build_py.py, src/distutils2/command/build_scripts.py, src/distutils2/command/clean.py, src/distutils2/command/cmd.py, src/distutils2/command/install.py, src/distutils2/command/install_data.py, src/distutils2/command/install_headers.py, src/distutils2/command/install_lib.py, src/distutils2/command/install_scripts.py diff --git a/src/distutils2/command/bdist_dumb.py b/src/distutils2/command/bdist_dumb.py --- a/src/distutils2/command/bdist_dumb.py +++ b/src/distutils2/command/bdist_dumb.py @@ -77,9 +77,7 @@ ("don't know how to create dumb built distributions " + "on platform %s") % os.name - self.set_undefined_options('bdist', - ('dist_dir', 'dist_dir'), - ('plat_name', 'plat_name')) + self.set_undefined_options('bdist', 'dist_dir', 'plat_name') def run(self): if not self.skip_build: diff --git a/src/distutils2/command/bdist_msi.py b/src/distutils2/command/bdist_msi.py --- a/src/distutils2/command/bdist_msi.py +++ b/src/distutils2/command/bdist_msi.py @@ -153,10 +153,7 @@ else: self.versions = list(self.all_versions) - self.set_undefined_options('bdist', - ('dist_dir', 'dist_dir'), - ('plat_name', 'plat_name'), - ) + self.set_undefined_options('bdist', 'dist_dir', 'plat_name') if self.pre_install_script: raise DistutilsOptionError, "the pre-install-script feature is not yet implemented" diff --git a/src/distutils2/command/bdist_wininst.py b/src/distutils2/command/bdist_wininst.py --- a/src/distutils2/command/bdist_wininst.py +++ b/src/distutils2/command/bdist_wininst.py @@ -100,10 +100,7 @@ " option must be specified" % (short_version,) self.target_version = short_version - self.set_undefined_options('bdist', - ('dist_dir', 'dist_dir'), - ('plat_name', 'plat_name'), - ) + self.set_undefined_options('bdist', 'dist_dir', 'plat_name') if self.install_script: for script in self.distribution.scripts: diff --git a/src/distutils2/command/build_clib.py b/src/distutils2/command/build_clib.py --- a/src/distutils2/command/build_clib.py +++ b/src/distutils2/command/build_clib.py @@ -76,9 +76,7 @@ self.set_undefined_options('build', ('build_temp', 'build_clib'), ('build_temp', 'build_temp'), - ('compiler', 'compiler'), - ('debug', 'debug'), - ('force', 'force')) + 'compiler', 'debug', 'force') self.libraries = self.distribution.libraries if self.libraries: diff --git a/src/distutils2/command/build_ext.py b/src/distutils2/command/build_ext.py --- a/src/distutils2/command/build_ext.py +++ b/src/distutils2/command/build_ext.py @@ -177,13 +177,8 @@ def finalize_options(self): self.set_undefined_options('build', - ('build_lib', 'build_lib'), - ('build_temp', 'build_temp'), - ('compiler', 'compiler'), - ('debug', 'debug'), - ('force', 'force'), - ('plat_name', 'plat_name'), - ) + 'build_lib', 'build_temp', 'compiler', + 'debug', 'force', 'plat_name') if self.package is None: self.package = self.distribution.ext_package diff --git a/src/distutils2/command/build_py.py b/src/distutils2/command/build_py.py --- a/src/distutils2/command/build_py.py +++ b/src/distutils2/command/build_py.py @@ -95,9 +95,7 @@ self._doctests_2to3 = [] def finalize_options(self): - self.set_undefined_options('build', - ('build_lib', 'build_lib'), - ('force', 'force')) + self.set_undefined_options('build', 'build_lib', 'force') # Get the distribution options that are aliases for build_py # options -- list of packages and list of modules. diff --git a/src/distutils2/command/build_scripts.py b/src/distutils2/command/build_scripts.py --- a/src/distutils2/command/build_scripts.py +++ b/src/distutils2/command/build_scripts.py @@ -40,8 +40,7 @@ def finalize_options (self): self.set_undefined_options('build', ('build_scripts', 'build_dir'), - ('force', 'force'), - ('executable', 'executable')) + 'force', 'executable') self.scripts = self.distribution.scripts def get_source_files(self): diff --git a/src/distutils2/command/clean.py b/src/distutils2/command/clean.py --- a/src/distutils2/command/clean.py +++ b/src/distutils2/command/clean.py @@ -40,13 +40,9 @@ self.all = None def finalize_options(self): - self.set_undefined_options('build', - ('build_base', 'build_base'), - ('build_lib', 'build_lib'), - ('build_scripts', 'build_scripts'), - ('build_temp', 'build_temp')) - self.set_undefined_options('bdist', - ('bdist_base', 'bdist_base')) + self.set_undefined_options('build', 'build_base', 'build_lib', + 'build_scripts', 'build_temp') + self.set_undefined_options('bdist', 'bdist_base') def run(self): # remove the build/temp. directory (unless it's already diff --git a/src/distutils2/command/cmd.py b/src/distutils2/command/cmd.py --- a/src/distutils2/command/cmd.py +++ b/src/distutils2/command/cmd.py @@ -297,26 +297,30 @@ else: return self.__class__.__name__ - def set_undefined_options(self, src_cmd, *option_pairs): - """Set the values of any "undefined" options from corresponding - option values in some other command object. "Undefined" here means - "is None", which is the convention used to indicate that an option - has not been changed between 'initialize_options()' and - 'finalize_options()'. Usually called from 'finalize_options()' for - options that depend on some other command rather than another - option of the same command. 'src_cmd' is the other command from - which option values will be taken (a command object will be created - for it if necessary); the remaining arguments are - '(src_option,dst_option)' tuples which mean "take the value of - 'src_option' in the 'src_cmd' command object, and copy it to - 'dst_option' in the current command object". + def set_undefined_options(self, src_cmd, *options): + """Set values of undefined options from another command. + + Undefined options are options set to None, which is the convention + used to indicate that an option has not been changed between + 'initialize_options()' and 'finalize_options()'. This method is + usually called from 'finalize_options()' for options that depend on + some other command rather than another option of the same command, + typically subcommands. + + The 'src_cmd' argument is the other command from which option values + will be taken (a command object will be created for it if necessary); + the remaining positional arguments are strings that give the name of + the option to set. If the name is different on the source and target + command, you can pass a tuple with '(name_on_source, name_on_dest)' so + that 'self.name_on_dest' will be set from 'src_cmd.name_on_source'. """ - - # Option_pairs: list of (src_option, dst_option) tuples - src_cmd_obj = self.distribution.get_command_obj(src_cmd) src_cmd_obj.ensure_finalized() - for (src_option, dst_option) in option_pairs: + for obj in options: + if isinstance(obj, tuple): + src_option, dst_option = obj + else: + src_option, dst_option = obj, obj if getattr(self, dst_option) is None: setattr(self, dst_option, getattr(src_cmd_obj, src_option)) diff --git a/src/distutils2/command/install.py b/src/distutils2/command/install.py --- a/src/distutils2/command/install.py +++ b/src/distutils2/command/install.py @@ -317,9 +317,7 @@ self.dump_dirs("after prepending root") # Find out the build directories, ie. where to install from. - self.set_undefined_options('build', - ('build_base', 'build_base'), - ('build_lib', 'build_lib')) + self.set_undefined_options('build', 'build_base', 'build_lib') # Punt on doc directories for now -- after all, we're punting on # documentation completely! diff --git a/src/distutils2/command/install_data.py b/src/distutils2/command/install_data.py --- a/src/distutils2/command/install_data.py +++ b/src/distutils2/command/install_data.py @@ -37,9 +37,7 @@ def finalize_options(self): self.set_undefined_options('install', ('install_data', 'install_dir'), - ('root', 'root'), - ('force', 'force'), - ) + 'root', 'force') def run(self): self.mkpath(self.install_dir) diff --git a/src/distutils2/command/install_headers.py b/src/distutils2/command/install_headers.py --- a/src/distutils2/command/install_headers.py +++ b/src/distutils2/command/install_headers.py @@ -29,8 +29,7 @@ def finalize_options(self): self.set_undefined_options('install', ('install_headers', 'install_dir'), - ('force', 'force')) - + 'force') def run(self): headers = self.distribution.headers diff --git a/src/distutils2/command/install_lib.py b/src/distutils2/command/install_lib.py --- a/src/distutils2/command/install_lib.py +++ b/src/distutils2/command/install_lib.py @@ -68,11 +68,7 @@ self.set_undefined_options('install', ('build_lib', 'build_dir'), ('install_lib', 'install_dir'), - ('force', 'force'), - ('compile', 'compile'), - ('optimize', 'optimize'), - ('skip_build', 'skip_build'), - ) + 'force', 'compile', 'optimize', 'skip_build') if self.compile is None: self.compile = 1 diff --git a/src/distutils2/command/install_scripts.py b/src/distutils2/command/install_scripts.py --- a/src/distutils2/command/install_scripts.py +++ b/src/distutils2/command/install_scripts.py @@ -36,9 +36,7 @@ self.set_undefined_options('build', ('build_scripts', 'build_dir')) self.set_undefined_options('install', ('install_scripts', 'install_dir'), - ('force', 'force'), - ('skip_build', 'skip_build'), - ) + 'force', 'skip_build') def run (self): if not self.skip_build: -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:46 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:46 +0200 Subject: [Python-checkins] distutils2: Merge from Konrad, with small fixes Message-ID: tarek.ziade pushed 5778145a172f to distutils2: http://hg.python.org/distutils2/rev/5778145a172f changeset: 438:5778145a172f parent: 436:70cc02f1c720 parent: 437:853e5b093ab8 user: ?ric Araujo date: Wed Aug 04 15:08:39 2010 +0200 summary: Merge from Konrad, with small fixes files: docs/source/commands.rst, docs/source/new_commands.rst, src/distutils2/command/upload.py, src/distutils2/command/upload_docs.py, src/distutils2/tests/test_upload.py diff --git a/docs/source/new_commands.rst b/docs/source/commands.rst rename from docs/source/new_commands.rst rename to docs/source/commands.rst --- a/docs/source/new_commands.rst +++ b/docs/source/commands.rst @@ -6,6 +6,50 @@ You might recognize some of them from other projects, like Distribute or Setuptools. +``upload`` - Upload source and/or binary distributions to PyPI +============================================================== + +The Python Package Index (PyPI) not only stores the package info, but also the +package data if the author of the package wishes to. The distutils command +:command:`upload` pushes the distribution files to PyPI. + +The command is invoked immediately after building one or more distribution +files. For example, the command :: + + python setup.py sdist bdist_wininst upload + +will cause the source distribution and the Windows installer to be uploaded to +PyPI. Note that these will be uploaded even if they are built using an earlier +invocation of :file:`setup.py`, but that only distributions named on the command +line for the invocation including the :command:`upload` command are uploaded. + +The :command:`upload` command uses the username, password, and repository URL +from the :file:`$HOME/.pypirc` file . If a :command:`register` command was +previously called in the same command, and if the password was entered in the +prompt, :command:`upload` will reuse the entered password. This is useful if +you do not want to store a clear text password in the :file:`$HOME/.pypirc` +file. + +The ``upload`` command has a few options worth noting: + +``--sign, -s`` + Sign each uploaded file using GPG (GNU Privacy Guard). The ``gpg`` program + must be available for execution on the system ``PATH``. + +``--identity=NAME, -i NAME`` + Specify the identity or key name for GPG to use when signing. The value of + this option will be passed through the ``--local-user`` option of the + ``gpg`` program. + +``--show-response`` + Display the full response text from server; this is useful for debugging + PyPI problems. + +``--repository=URL, -r URL`` + The URL of the repository to upload to. Defaults to + http://pypi.python.org/pypi (i.e., the main PyPI installation). + + ``upload_docs`` - Upload package documentation to PyPI ====================================================== @@ -40,7 +84,7 @@ python setup.py upload_docs --upload-dir=docs/build/html -As with any other ``setuptools`` based command, you can define useful +As with any other command, you can define useful defaults in the ``setup.cfg`` of your Python project, e.g.: .. code-block:: ini diff --git a/docs/source/index.rst b/docs/source/index.rst --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -14,7 +14,7 @@ metadata pkgutil depgraph - new_commands + commands test_framework pypi version diff --git a/src/distutils2/command/upload.py b/src/distutils2/command/upload.py --- a/src/distutils2/command/upload.py +++ b/src/distutils2/command/upload.py @@ -35,6 +35,8 @@ "sign files to upload using gpg"), ('identity=', 'i', "GPG identity used to sign files"), + ('upload-docs', None, + "upload documentation too"), ] boolean_options = ['show-response', 'sign'] @@ -48,6 +50,7 @@ self.show_response = 0 self.sign = False self.identity = None + self.upload_docs = False def finalize_options(self): if self.repository is None: @@ -75,6 +78,12 @@ raise DistutilsOptionError("No dist file created in earlier command") for command, pyversion, filename in self.distribution.dist_files: self.upload_file(command, pyversion, filename) + if self.upload_docs: + upload_docs = self.get_finalized_command("upload_docs") + upload_docs.repository = self.repository + upload_docs.username = self.username + upload_docs.password = self.password + upload_docs.run() # XXX to be refactored with register.post_to_server def upload_file(self, command, pyversion, filename): diff --git a/src/distutils2/command/upload_docs.py b/src/distutils2/command/upload_docs.py --- a/src/distutils2/command/upload_docs.py +++ b/src/distutils2/command/upload_docs.py @@ -64,7 +64,7 @@ self.repository = None self.realm = None self.show_response = 0 - self.upload_dir = "build/docs" + self.upload_dir = None self.username = '' self.password = '' @@ -73,7 +73,7 @@ self.repository = DEFAULT_REPOSITORY if self.realm is None: self.realm = DEFAULT_REALM - if self.upload_dir == None: + if self.upload_dir is None: build = self.get_finalized_command('build') self.upload_dir = os.path.join(build.build_base, "docs") self.announce('Using upload directory %s' % self.upload_dir) diff --git a/src/distutils2/tests/test_upload.py b/src/distutils2/tests/test_upload.py --- a/src/distutils2/tests/test_upload.py +++ b/src/distutils2/tests/test_upload.py @@ -101,6 +101,38 @@ self.assertEqual(handler.command, 'POST') self.assertNotIn('\n', headers['authorization']) + def test_upload_docs(self): + path = os.path.join(self.tmp_dir, 'xxx') + self.write_file(path) + command, pyversion, filename = 'xxx', '2.6', path + dist_files = [(command, pyversion, filename)] + docs_path = os.path.join(self.tmp_dir, "build", "docs") + os.makedirs(docs_path) + self.write_file(os.path.join(docs_path, "index.html"), "yellow") + self.write_file(self.rc, PYPIRC) + + # lets run it + pkg_dir, dist = self.create_dist(dist_files=dist_files, author=u'd??d??') + + cmd = upload(dist) + cmd.get_finalized_command("build").run() + cmd.upload_docs = True + cmd.ensure_finalized() + cmd.repository = self.pypi.full_address + try: + prev_dir = os.getcwd() + os.chdir(self.tmp_dir) + cmd.run() + finally: + os.chdir(prev_dir) + + handler, request_data = self.pypi.requests[-1] + action, name, content =\ + request_data.split("----------------GHSKFJDLGDS7543FJKLFHRE75642756743254")[1:4] + + self.assertIn('name=":action"', action) + self.assertIn("doc_upload", action) + def test_suite(): return unittest.makeSuite(UploadTestCase) -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:46 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:46 +0200 Subject: [Python-checkins] distutils2: Add command post-hooks Message-ID: tarek.ziade pushed 593b7db7a5b4 to distutils2: http://hg.python.org/distutils2/rev/593b7db7a5b4 changeset: 441:593b7db7a5b4 user: Konrad Delong date: Thu Aug 05 15:48:12 2010 +0200 summary: Add command post-hooks files: docs/source/command_hooks.rst, docs/source/index.rst, src/distutils2/command/cmd.py, src/distutils2/dist.py, src/distutils2/tests/test_dist.py, src/distutils2/tests/test_util.py, src/distutils2/util.py diff --git a/docs/source/command_hooks.rst b/docs/source/command_hooks.rst new file mode 100644 --- /dev/null +++ b/docs/source/command_hooks.rst @@ -0,0 +1,31 @@ +============= +Command hooks +============= + +Distutils2 provides a way of extending its commands by the use of pre- and +post- command hooks. The hooks are simple Python functions (or any callable +objects) and are specified in the config file using their full qualified names. +The pre-hooks are run after the command is finalized (its options are +processed), but before it is run. The post-hooks are run after the command +itself. Both types of hooks receive an instance of the command object. + +Sample usage of hooks +===================== + +Firstly, you need to make sure your hook is present in the path. This is usually +done by dropping them to the same folder where `setup.py` file lives :: + + # file: myhooks.py + def my_install_hook(install_cmd): + print "Oh la la! Someone is installing my project!" + +Then, you need to point to it in your `setup.cfg` file, under the appropriate +command section :: + + [install] + pre-hook.project = myhooks.my_install_hook + +The hooks defined in different config files (system-wide, user-wide and +package-wide) do not override each other as long as they are specified with +different aliases (additional names after the dot). The alias in the example +above is ``project``. diff --git a/docs/source/index.rst b/docs/source/index.rst --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -15,6 +15,7 @@ pkgutil depgraph commands + command_hooks test_framework pypi version diff --git a/src/distutils2/command/cmd.py b/src/distutils2/command/cmd.py --- a/src/distutils2/command/cmd.py +++ b/src/distutils2/command/cmd.py @@ -51,6 +51,12 @@ # defined. The canonical example is the "install" command. sub_commands = [] + # Pre and post command hooks are run just before or just after the command + # itself. They are simple functions that receive the command instance. They + # should be specified as dotted strings. + pre_hook = None + post_hook = None + # -- Creation/initialization methods ------------------------------- diff --git a/src/distutils2/dist.py b/src/distutils2/dist.py --- a/src/distutils2/dist.py +++ b/src/distutils2/dist.py @@ -18,7 +18,7 @@ from distutils2.errors import (DistutilsOptionError, DistutilsArgError, DistutilsModuleError, DistutilsClassError) from distutils2.fancy_getopt import FancyGetopt, translate_longopt -from distutils2.util import check_environ, strtobool +from distutils2.util import check_environ, strtobool, resolve_dotted_name from distutils2 import log from distutils2.metadata import DistributionMetadata @@ -28,7 +28,6 @@ # to look for a Python module named after the command. command_re = re.compile (r'^[a-zA-Z]([a-zA-Z0-9_]*)$') - class Distribution(object): """The core of the Distutils. Most of the work hiding behind 'setup' is really done within a Distribution instance, which farms the work out @@ -116,7 +115,7 @@ ('use-2to3', None, "use 2to3 to make source python 3.x compatible"), ('convert-2to3-doctests', None, - "use 2to3 to convert doctests in seperate text files"), + "use 2to3 to convert doctests in seperate text files"), ] display_option_names = map(lambda x: translate_longopt(x[0]), display_options) @@ -382,7 +381,19 @@ if opt != '__name__': val = parser.get(section,opt) opt = opt.replace('-', '_') - opt_dict[opt] = (filename, val) + + # ... although practicality beats purity :( + if opt.startswith("pre_hook.") or opt.startswith("post_hook."): + hook_type, alias = opt.split(".") + # Hooks are somewhat exceptional, as they are + # gathered from many config files (not overriden as + # other options). + # The option_dict expects {"command": ("filename", # "value")} + # so for hooks, we store only the last config file processed + hook_dict = opt_dict.setdefault(hook_type, (filename, {}))[1] + hook_dict[alias] = val + else: + opt_dict[opt] = (filename, val) # Make the RawConfigParser forget everything (so we retain # the original filenames that options come from) @@ -583,7 +594,7 @@ objects. """ if getattr(self, 'convert_2to3_doctests', None): - self.convert_2to3_doctests = [os.path.join(p) + self.convert_2to3_doctests = [os.path.join(p) for p in self.convert_2to3_doctests] else: self.convert_2to3_doctests = [] @@ -948,13 +959,23 @@ if self.have_run.get(command): return - log.info("running %s", command) cmd_obj = self.get_command_obj(command) cmd_obj.ensure_finalized() + self.run_command_hooks(cmd_obj, 'pre_hook') + log.info("running %s", command) cmd_obj.run() + self.run_command_hooks(cmd_obj, 'post_hook') self.have_run[command] = 1 + def run_command_hooks(self, cmd_obj, hook_kind): + hooks = getattr(cmd_obj, hook_kind) + if hooks is None: + return + for hook in hooks.values(): + hook_func = resolve_dotted_name(hook) + hook_func(cmd_obj) + # -- Distribution query methods ------------------------------------ def has_pure_modules(self): diff --git a/src/distutils2/tests/test_dist.py b/src/distutils2/tests/test_dist.py --- a/src/distutils2/tests/test_dist.py +++ b/src/distutils2/tests/test_dist.py @@ -240,6 +240,49 @@ # make sure --no-user-cfg disables the user cfg file self.assertEqual(len(all_files)-1, len(files)) + def test_special_hooks_parsing(self): + temp_home = self.mkdtemp() + config_files = [os.path.join(temp_home, "config1.cfg"), + os.path.join(temp_home, "config2.cfg")] + + # Store two aliased hooks in config files + self.write_file((temp_home, "config1.cfg"), '[test_dist]\npre-hook.a = type') + self.write_file((temp_home, "config2.cfg"), '[test_dist]\npre-hook.b = type') + + sys.argv.extend(["--command-packages", + "distutils2.tests", + "test_dist"]) + cmd = self.create_distribution(config_files).get_command_obj("test_dist") + self.assertEqual(cmd.pre_hook, {"a": 'type', "b": 'type'}) + + + def test_hooks_get_run(self): + temp_home = self.mkdtemp() + config_file = os.path.join(temp_home, "config1.cfg") + + self.write_file((temp_home, "config1.cfg"), textwrap.dedent(''' + [test_dist] + pre-hook.test = distutils2.tests.test_dist.DistributionTestCase.log_pre_call + post-hook.test = distutils2.tests.test_dist.DistributionTestCase.log_post_call''')) + + sys.argv.extend(["--command-packages", + "distutils2.tests", + "test_dist"]) + d = self.create_distribution([config_file]) + cmd = d.get_command_obj("test_dist") + + # prepare the call recorders + record = [] + DistributionTestCase.log_pre_call = staticmethod(lambda _cmd: record.append(('pre', _cmd))) + DistributionTestCase.log_post_call = staticmethod(lambda _cmd: record.append(('post', _cmd))) + test_dist.run = lambda _cmd: record.append(('run', _cmd)) + test_dist.finalize_options = lambda _cmd: record.append(('finalize_options', _cmd)) + + d.run_command('test_dist') + self.assertEqual(record, [('finalize_options', cmd), + ('pre', cmd), + ('run', cmd), + ('post', cmd)]) class MetadataTestCase(support.TempdirManager, support.EnvironGuard, unittest.TestCase): diff --git a/src/distutils2/tests/test_util.py b/src/distutils2/tests/test_util.py --- a/src/distutils2/tests/test_util.py +++ b/src/distutils2/tests/test_util.py @@ -18,7 +18,7 @@ _find_exe_version, _MAC_OS_X_LD_VERSION, byte_compile, find_packages, spawn, find_executable, _nt_quote_args, get_pypirc_path, generate_pypirc, - read_pypirc) + read_pypirc, resolve_dotted_name) from distutils2 import util from distutils2.tests import support @@ -342,6 +342,16 @@ res = find_packages([root], ['pkg1.pkg2']) self.assertEqual(set(res), set(['pkg1', 'pkg5', 'pkg1.pkg3', 'pkg1.pkg3.pkg6'])) + def test_resolve_dotted_name(self): + self.assertEqual(UtilTestCase, resolve_dotted_name("distutils2.tests.test_util.UtilTestCase")) + self.assertEqual(UtilTestCase.test_resolve_dotted_name, + resolve_dotted_name("distutils2.tests.test_util.UtilTestCase.test_resolve_dotted_name")) + + self.assertRaises(ImportError, resolve_dotted_name, + "distutils2.tests.test_util.UtilTestCaseNot") + self.assertRaises(ImportError, resolve_dotted_name, + "distutils2.tests.test_util.UtilTestCase.nonexistent_attribute") + @unittest.skipIf(sys.version < '2.6', 'requires Python 2.6 or higher') def test_run_2to3_on_code(self): content = "print 'test'" diff --git a/src/distutils2/util.py b/src/distutils2/util.py --- a/src/distutils2/util.py +++ b/src/distutils2/util.py @@ -633,6 +633,24 @@ return packages +def resolve_dotted_name(dotted_name): + module_name, rest = dotted_name.split('.')[0], dotted_name.split('.')[1:] + while len(rest) > 0: + try: + ret = __import__(module_name) + break + except ImportError: + if rest == []: + raise + module_name += ('.' + rest[0]) + rest = rest[1:] + while len(rest) > 0: + try: + ret = getattr(ret, rest.pop(0)) + except AttributeError: + raise ImportError + return ret + # utility functions for 2to3 support def run_2to3(files, doctests_only=False, fixer_names=None, options=None, -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:46 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:46 +0200 Subject: [Python-checkins] distutils2: Add a way to construct DistributionMetadata from a dict. Message-ID: tarek.ziade pushed d67ed881c17d to distutils2: http://hg.python.org/distutils2/rev/d67ed881c17d changeset: 448:d67ed881c17d user: Alexis Metaireau date: Tue Jul 20 15:26:18 2010 +0200 summary: Add a way to construct DistributionMetadata from a dict. files: src/distutils2/metadata.py diff --git a/src/distutils2/metadata.py b/src/distutils2/metadata.py --- a/src/distutils2/metadata.py +++ b/src/distutils2/metadata.py @@ -183,9 +183,12 @@ class DistributionMetadata(object): """Distribution meta-data class (1.0 or 1.2). + + if from_dict attribute is set, all key/values pairs will be sent to the + "set" method, building the metadata from the dict. """ def __init__(self, path=None, platform_dependent=False, - execution_context=None, fileobj=None): + execution_context=None, fileobj=None, from_dict=None): self._fields = {} self.version = None self.docutils_support = _HAS_DOCUTILS @@ -195,6 +198,8 @@ elif fileobj is not None: self.read_file(fileobj) self.execution_context = execution_context + if from_dict: + self.set_from_dict(from_dict) def _set_best_version(self): self.version = _best_version(self._fields) @@ -330,6 +335,21 @@ for value in values: self._write_field(fileobject, field, value) + def set_from_dict(self, from_dict): + """Set metadata values from the given dict. + + If overwrite is set to False, just add metadata values that are + actually not defined. + + If there is existing values in conflict with the dictionary ones, the + dictionary values prevails. + + Empty values (e.g. None and []) are not setted this way. + """ + for key, value in from_dict.items(): + if value not in ([], None): + self.set(key, value) + def set(self, name, value): """Controls then sets a metadata field""" name = self._convert_name(name) -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:46 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:46 +0200 Subject: [Python-checkins] distutils2: Merge upstream Message-ID: tarek.ziade pushed 196d1ecad12b to distutils2: http://hg.python.org/distutils2/rev/196d1ecad12b changeset: 446:196d1ecad12b parent: 445:1d2f4bc24ad3 parent: 382:733c3bdaa25e user: Alexis Metaireau date: Mon Jul 19 15:29:26 2010 +0200 summary: Merge upstream files: diff --git a/.hgignore b/.hgignore --- a/.hgignore +++ b/.hgignore @@ -6,3 +6,4 @@ MANIFEST dist/ *.swp +.coverage \ No newline at end of file diff --git a/src/CONTRIBUTORS.txt b/src/CONTRIBUTORS.txt --- a/src/CONTRIBUTORS.txt +++ b/src/CONTRIBUTORS.txt @@ -13,15 +13,18 @@ - Pior Bastida - Titus Brown - Nicolas Cadou +- Konrad Delong - Josip Djolonga - Yannick Gringas - Jeremy Kloth - Martin von L??wis - Carl Meyer -- Alexis Metaireau +- Alexis M??taireau +- Zubin Mithra - Michael Mulich -- George Peris +- George Peristerakis - Sean Reifschneider +- Luis Rojas - Erik Rose - Brian Rosner - Alexandre Vassalotti diff --git a/src/distutils2/command/bdist_dumb.py b/src/distutils2/command/bdist_dumb.py --- a/src/distutils2/command/bdist_dumb.py +++ b/src/distutils2/command/bdist_dumb.py @@ -115,8 +115,9 @@ % (repr(install.install_base), repr(install.install_platbase))) else: - archive_root = os.path.join(self.bdist_dir, - ensure_relative(install.install_base)) + archive_root = os.path.join( + self.bdist_dir, + self._ensure_relative(install.install_base)) # Make the archive filename = self.make_archive(pseudoinstall_root, @@ -135,3 +136,9 @@ else: rmtree(self.bdist_dir) + def _ensure_relative(self, path): + # copied from dir_util, deleted + drive, path = os.path.splitdrive(path) + if path[0:1] == os.sep: + path = drive + path[1:] + return path diff --git a/src/distutils2/command/bdist_wininst.py b/src/distutils2/command/bdist_wininst.py --- a/src/distutils2/command/bdist_wininst.py +++ b/src/distutils2/command/bdist_wininst.py @@ -8,6 +8,7 @@ import sys import os import string +from shutil import rmtree try: from sysconfig import get_python_version except ImportError: diff --git a/src/distutils2/command/build_py.py b/src/distutils2/command/build_py.py --- a/src/distutils2/command/build_py.py +++ b/src/distutils2/command/build_py.py @@ -9,6 +9,7 @@ import logging from glob import glob +import distutils2 from distutils2.core import Command from distutils2.errors import DistutilsOptionError, DistutilsFileError from distutils2.util import convert_path diff --git a/src/distutils2/command/cmd.py b/src/distutils2/command/cmd.py --- a/src/distutils2/command/cmd.py +++ b/src/distutils2/command/cmd.py @@ -6,7 +6,7 @@ __revision__ = "$Id: cmd.py 75192 2009-10-02 23:49:48Z tarek.ziade $" -import sys, os, re +import os, re from distutils2.errors import DistutilsOptionError from distutils2 import util from distutils2 import log @@ -447,7 +447,7 @@ # If 'outfile' must be regenerated (either because it doesn't # exist, is out-of-date, or the 'force' flag is true) then # perform the action that presumably regenerates it - if self.force or dep_util.newer_group(infiles, outfile): + if self.force or util.newer_group(infiles, outfile): self.execute(func, args, exec_msg, level) # Otherwise, print the "skip" message diff --git a/src/distutils2/command/sdist.py b/src/distutils2/command/sdist.py --- a/src/distutils2/command/sdist.py +++ b/src/distutils2/command/sdist.py @@ -18,12 +18,11 @@ from distutils2._backport.shutil import get_archive_formats from distutils2.core import Command -from distutils2 import util from distutils2.errors import (DistutilsPlatformError, DistutilsOptionError, DistutilsTemplateError) from distutils2.manifest import Manifest from distutils2 import log -from distutils2.util import convert_path, newer +from distutils2.util import convert_path def show_formats(): """Print all possible values for the 'formats' option (used by diff --git a/src/distutils2/command/upload_docs.py b/src/distutils2/command/upload_docs.py --- a/src/distutils2/command/upload_docs.py +++ b/src/distutils2/command/upload_docs.py @@ -1,4 +1,4 @@ -import base64, httplib, os.path, socket, tempfile, urlparse, zipfile +import base64, httplib, os.path, socket, urlparse, zipfile from cStringIO import StringIO from distutils2 import log from distutils2.command.upload import upload @@ -81,7 +81,6 @@ raise DistutilsFileError(mesg % upload_dir) def run(self): - tmp_dir = tempfile.mkdtemp() name = self.distribution.metadata['Name'] zip_file = zip_dir(self.upload_dir) @@ -124,7 +123,7 @@ elif r.status == 301: location = r.getheader('Location') if location is None: - location = 'http://packages.python.org/%s/' % meta.get_name() + location = 'http://packages.python.org/%s/' % name self.announce('Upload successful. Visit %s' % location, log.INFO) else: diff --git a/src/distutils2/converter/fixers/fix_imports.py b/src/distutils2/converter/fixers/fix_imports.py --- a/src/distutils2/converter/fixers/fix_imports.py +++ b/src/distutils2/converter/fixers/fix_imports.py @@ -36,11 +36,16 @@ pattern = [] next = imp.next_sibling while next is not None: + # Get the first child if we have a Node + if not hasattr(next, "value"): + next = next.children[0] pattern.append(next.value) if not hasattr(next, "next_sibling"): next.next_sibling = next.get_next_sibling() next = next.next_sibling - if pattern == ['import', 'setup']: + + if set(pattern).issubset(set( + ['import', ',', 'setup', 'find_packages'])): imp.value = 'distutils2.core' imp.changed() diff --git a/src/distutils2/extension.py b/src/distutils2/extension.py --- a/src/distutils2/extension.py +++ b/src/distutils2/extension.py @@ -5,14 +5,8 @@ __revision__ = "$Id: extension.py 77704 2010-01-23 09:23:15Z tarek.ziade $" -import os import warnings -try: - import sysconfig -except ImportError: - from distutils2._backport import sysconfig - # This class is really only used by the "build_ext" command, so it might # make sense to put it in distutils.command.build_ext. However, that # module is already big enough, and I want to make this class a bit more diff --git a/src/distutils2/tests/conversions/05_after.py b/src/distutils2/tests/conversions/05_after.py new file mode 100644 --- /dev/null +++ b/src/distutils2/tests/conversions/05_after.py @@ -0,0 +1,137 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright (C) 2003-2009 Edgewall Software +# All rights reserved. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at http://trac.edgewall.org/wiki/TracLicense. +# +# This software consists of voluntary contributions made by many +# individuals. For the exact contribution history, see the revision +# history and logs, available at http://trac.edgewall.org/log/. + +from distutils2.core import setup, find_packages + +extra = {} + +try: + import babel + + extractors = [ + ('**.py', 'python', None), + ('**/templates/**.html', 'genshi', None), + ('**/templates/**.txt', 'genshi', + {'template_class': 'genshi.template:NewTextTemplate'}), + ] + extra['message_extractors'] = { + 'trac': extractors, + 'tracopt': extractors, + } + + from trac.util.dist import get_l10n_js_cmdclass + extra['cmdclass'] = get_l10n_js_cmdclass() + +except ImportError, e: + pass + +setup( + name = 'Trac', + version = '0.12.1', + summary = 'Integrated SCM, wiki, issue tracker and project environment', + description = """ +Trac is a minimalistic web-based software project management and bug/issue +tracking system. It provides an interface to the Subversion revision control +systems, an integrated wiki, flexible issue tracking and convenient report +facilities. +""", + author = 'Edgewall Software', + author_email = 'info at edgewall.com', + license = 'BSD', + home_page = 'http://trac.edgewall.org/', + download_url = 'http://trac.edgewall.org/wiki/TracDownload', + classifiers = [ + 'Environment :: Web Environment', + 'Framework :: Trac', + 'Intended Audience :: Developers', + 'License :: OSI Approved :: BSD License', + 'Operating System :: OS Independent', + 'Programming Language :: Python', + 'Topic :: Software Development :: Bug Tracking', + 'Topic :: Software Development :: Version Control', + ], + + packages = find_packages(exclude=['*.tests']), + package_data = { + '': ['templates/*'], + 'trac': ['htdocs/*.*', 'htdocs/README', 'htdocs/js/*.*', + 'htdocs/js/messages/*.*', 'htdocs/css/*.*', + 'htdocs/guide/*', 'locale/*/LC_MESSAGES/messages.mo'], + 'trac.wiki': ['default-pages/*'], + 'trac.ticket': ['workflows/*.ini'], + }, + + test_suite = 'trac.test.suite', + zip_safe = True, + + requires_dist = [ + 'setuptools>=0.6b1', + 'Genshi>=0.6', + ], + extras_require = { + 'Babel': ['Babel>=0.9.5'], + 'Pygments': ['Pygments>=0.6'], + 'reST': ['docutils>=0.3'], + 'SilverCity': ['SilverCity>=0.9.4'], + 'Textile': ['textile>=2.0'], + }, + + entry_points = """ + [console_scripts] + trac-admin = trac.admin.console:run + tracd = trac.web.standalone:main + + [trac.plugins] + trac.about = trac.about + trac.admin.console = trac.admin.console + trac.admin.web_ui = trac.admin.web_ui + trac.attachment = trac.attachment + trac.db.mysql = trac.db.mysql_backend + trac.db.postgres = trac.db.postgres_backend + trac.db.sqlite = trac.db.sqlite_backend + trac.mimeview.patch = trac.mimeview.patch + trac.mimeview.pygments = trac.mimeview.pygments[Pygments] + trac.mimeview.rst = trac.mimeview.rst[reST] + trac.mimeview.silvercity = trac.mimeview.silvercity[SilverCity] + trac.mimeview.txtl = trac.mimeview.txtl[Textile] + trac.prefs = trac.prefs.web_ui + trac.search = trac.search.web_ui + trac.ticket.admin = trac.ticket.admin + trac.ticket.query = trac.ticket.query + trac.ticket.report = trac.ticket.report + trac.ticket.roadmap = trac.ticket.roadmap + trac.ticket.web_ui = trac.ticket.web_ui + trac.timeline = trac.timeline.web_ui + trac.versioncontrol.admin = trac.versioncontrol.admin + trac.versioncontrol.svn_authz = trac.versioncontrol.svn_authz + trac.versioncontrol.svn_fs = trac.versioncontrol.svn_fs + trac.versioncontrol.svn_prop = trac.versioncontrol.svn_prop + trac.versioncontrol.web_ui = trac.versioncontrol.web_ui + trac.web.auth = trac.web.auth + trac.web.session = trac.web.session + trac.wiki.admin = trac.wiki.admin + trac.wiki.interwiki = trac.wiki.interwiki + trac.wiki.macros = trac.wiki.macros + trac.wiki.web_ui = trac.wiki.web_ui + trac.wiki.web_api = trac.wiki.web_api + tracopt.mimeview.enscript = tracopt.mimeview.enscript + tracopt.mimeview.php = tracopt.mimeview.php + tracopt.perm.authz_policy = tracopt.perm.authz_policy + tracopt.perm.config_perm_provider = tracopt.perm.config_perm_provider + tracopt.ticket.commit_updater = tracopt.ticket.commit_updater + tracopt.ticket.deleter = tracopt.ticket.deleter + """, + + **extra +) diff --git a/src/distutils2/tests/conversions/05_before.py b/src/distutils2/tests/conversions/05_before.py new file mode 100755 --- /dev/null +++ b/src/distutils2/tests/conversions/05_before.py @@ -0,0 +1,137 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright (C) 2003-2009 Edgewall Software +# All rights reserved. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at http://trac.edgewall.org/wiki/TracLicense. +# +# This software consists of voluntary contributions made by many +# individuals. For the exact contribution history, see the revision +# history and logs, available at http://trac.edgewall.org/log/. + +from setuptools import setup, find_packages + +extra = {} + +try: + import babel + + extractors = [ + ('**.py', 'python', None), + ('**/templates/**.html', 'genshi', None), + ('**/templates/**.txt', 'genshi', + {'template_class': 'genshi.template:NewTextTemplate'}), + ] + extra['message_extractors'] = { + 'trac': extractors, + 'tracopt': extractors, + } + + from trac.util.dist import get_l10n_js_cmdclass + extra['cmdclass'] = get_l10n_js_cmdclass() + +except ImportError, e: + pass + +setup( + name = 'Trac', + version = '0.12.1', + description = 'Integrated SCM, wiki, issue tracker and project environment', + long_description = """ +Trac is a minimalistic web-based software project management and bug/issue +tracking system. It provides an interface to the Subversion revision control +systems, an integrated wiki, flexible issue tracking and convenient report +facilities. +""", + author = 'Edgewall Software', + author_email = 'info at edgewall.com', + license = 'BSD', + url = 'http://trac.edgewall.org/', + download_url = 'http://trac.edgewall.org/wiki/TracDownload', + classifiers = [ + 'Environment :: Web Environment', + 'Framework :: Trac', + 'Intended Audience :: Developers', + 'License :: OSI Approved :: BSD License', + 'Operating System :: OS Independent', + 'Programming Language :: Python', + 'Topic :: Software Development :: Bug Tracking', + 'Topic :: Software Development :: Version Control', + ], + + packages = find_packages(exclude=['*.tests']), + package_data = { + '': ['templates/*'], + 'trac': ['htdocs/*.*', 'htdocs/README', 'htdocs/js/*.*', + 'htdocs/js/messages/*.*', 'htdocs/css/*.*', + 'htdocs/guide/*', 'locale/*/LC_MESSAGES/messages.mo'], + 'trac.wiki': ['default-pages/*'], + 'trac.ticket': ['workflows/*.ini'], + }, + + test_suite = 'trac.test.suite', + zip_safe = True, + + install_requires = [ + 'setuptools>=0.6b1', + 'Genshi>=0.6', + ], + extras_require = { + 'Babel': ['Babel>=0.9.5'], + 'Pygments': ['Pygments>=0.6'], + 'reST': ['docutils>=0.3'], + 'SilverCity': ['SilverCity>=0.9.4'], + 'Textile': ['textile>=2.0'], + }, + + entry_points = """ + [console_scripts] + trac-admin = trac.admin.console:run + tracd = trac.web.standalone:main + + [trac.plugins] + trac.about = trac.about + trac.admin.console = trac.admin.console + trac.admin.web_ui = trac.admin.web_ui + trac.attachment = trac.attachment + trac.db.mysql = trac.db.mysql_backend + trac.db.postgres = trac.db.postgres_backend + trac.db.sqlite = trac.db.sqlite_backend + trac.mimeview.patch = trac.mimeview.patch + trac.mimeview.pygments = trac.mimeview.pygments[Pygments] + trac.mimeview.rst = trac.mimeview.rst[reST] + trac.mimeview.silvercity = trac.mimeview.silvercity[SilverCity] + trac.mimeview.txtl = trac.mimeview.txtl[Textile] + trac.prefs = trac.prefs.web_ui + trac.search = trac.search.web_ui + trac.ticket.admin = trac.ticket.admin + trac.ticket.query = trac.ticket.query + trac.ticket.report = trac.ticket.report + trac.ticket.roadmap = trac.ticket.roadmap + trac.ticket.web_ui = trac.ticket.web_ui + trac.timeline = trac.timeline.web_ui + trac.versioncontrol.admin = trac.versioncontrol.admin + trac.versioncontrol.svn_authz = trac.versioncontrol.svn_authz + trac.versioncontrol.svn_fs = trac.versioncontrol.svn_fs + trac.versioncontrol.svn_prop = trac.versioncontrol.svn_prop + trac.versioncontrol.web_ui = trac.versioncontrol.web_ui + trac.web.auth = trac.web.auth + trac.web.session = trac.web.session + trac.wiki.admin = trac.wiki.admin + trac.wiki.interwiki = trac.wiki.interwiki + trac.wiki.macros = trac.wiki.macros + trac.wiki.web_ui = trac.wiki.web_ui + trac.wiki.web_api = trac.wiki.web_api + tracopt.mimeview.enscript = tracopt.mimeview.enscript + tracopt.mimeview.php = tracopt.mimeview.php + tracopt.perm.authz_policy = tracopt.perm.authz_policy + tracopt.perm.config_perm_provider = tracopt.perm.config_perm_provider + tracopt.ticket.commit_updater = tracopt.ticket.commit_updater + tracopt.ticket.deleter = tracopt.ticket.deleter + """, + + **extra +) diff --git a/src/distutils2/tests/test_build_ext.py b/src/distutils2/tests/test_build_ext.py --- a/src/distutils2/tests/test_build_ext.py +++ b/src/distutils2/tests/test_build_ext.py @@ -368,4 +368,4 @@ else: return unittest.makeSuite(BuildExtTestCase) if __name__ == '__main__': - distsutils2.tests.run_unittest(test_suite()) + distutils2.tests.run_unittest(test_suite()) diff --git a/src/distutils2/tests/test_depgraph.py b/src/distutils2/tests/test_depgraph.py --- a/src/distutils2/tests/test_depgraph.py +++ b/src/distutils2/tests/test_depgraph.py @@ -10,7 +10,7 @@ import re try: import cStringIO as StringIO -except ImportErorr: +except ImportError: import StringIO class DepGraphTestCase(support.LoggingSilencer, diff --git a/src/distutils2/tests/test_manifest.py b/src/distutils2/tests/test_manifest.py --- a/src/distutils2/tests/test_manifest.py +++ b/src/distutils2/tests/test_manifest.py @@ -3,6 +3,7 @@ import sys import logging +from distutils2.tests import run_unittest from distutils2.tests import support from distutils2.tests.support import unittest from distutils2.manifest import Manifest diff --git a/src/distutils2/tests/test_metadata.py b/src/distutils2/tests/test_metadata.py --- a/src/distutils2/tests/test_metadata.py +++ b/src/distutils2/tests/test_metadata.py @@ -5,7 +5,10 @@ from distutils2.metadata import (DistributionMetadata, _interpret, PKG_INFO_PREFERRED_VERSION) +from distutils2.tests import run_unittest from distutils2.tests.support import unittest, LoggingSilencer +from distutils2.errors import (MetadataConflictError, + MetadataUnrecognizedVersionError) class DistributionMetadataTestCase(LoggingSilencer, unittest.TestCase): @@ -125,6 +128,10 @@ metadata['Obsoletes-Dist'] = 'ok' self.assertEqual(metadata['Metadata-Version'], '1.2') + self.assertRaises(MetadataConflictError, metadata.set, + 'Obsoletes', 'ok') + + del metadata['Obsoletes'] del metadata['Obsoletes-Dist'] metadata['Version'] = '1' self.assertEqual(metadata['Metadata-Version'], '1.0') @@ -139,6 +146,9 @@ metadata.read_file(StringIO(open(PKG_INFO).read())) self.assertEqual(metadata['Metadata-Version'], '1.1') + metadata.version = '1.618' + self.assertRaises(MetadataUnrecognizedVersionError, metadata.keys) + def test_warnings(self): metadata = DistributionMetadata() diff --git a/src/distutils2/tests/test_pypi_versions.py b/src/distutils2/tests/test_pypi_versions.py --- a/src/distutils2/tests/test_pypi_versions.py +++ b/src/distutils2/tests/test_pypi_versions.py @@ -19,6 +19,7 @@ import os.path from distutils2.version import suggest_normalized_version +from distutils2.tests import run_unittest from distutils2.tests.support import unittest def test_pypi(): @@ -52,7 +53,7 @@ print "Saving package info..." f = open(INDEX_PICKLE_FILE, 'wb') try: - pickle.dump(package_info, o) + pickle.dump(package_info, f) finally: f.close() diff --git a/src/distutils2/tests/test_util.py b/src/distutils2/tests/test_util.py --- a/src/distutils2/tests/test_util.py +++ b/src/distutils2/tests/test_util.py @@ -4,6 +4,8 @@ from copy import copy from StringIO import StringIO import subprocess +import tempfile +import time from distutils2.errors import (DistutilsPlatformError, DistutilsByteCompileError, @@ -257,7 +259,10 @@ def test_newer(self): self.assertRaises(DistutilsFileError, util.newer, 'xxx', 'xxx') - + self.newer_f1 = tempfile.NamedTemporaryFile() + time.sleep(1) + self.newer_f2 = tempfile.NamedTemporaryFile() + self.assertTrue(util.newer(self.newer_f2.name, self.newer_f1.name)) def test_find_packages(self): # let's create a structure we want to scan: diff --git a/src/distutils2/tests/test_version.py b/src/distutils2/tests/test_version.py --- a/src/distutils2/tests/test_version.py +++ b/src/distutils2/tests/test_version.py @@ -3,7 +3,7 @@ import os from distutils2.version import NormalizedVersion as V -from distutils2.version import IrrationalVersionError +from distutils2.version import HugeMajorVersionNumError, IrrationalVersionError from distutils2.version import suggest_normalized_version as suggest from distutils2.version import VersionPredicate from distutils2.tests.support import unittest @@ -22,6 +22,10 @@ (V('1.0.dev345'), '1.0.dev345'), (V('1.0.post456.dev623'), '1.0.post456.dev623')) + def test_repr(self): + + self.assertEqual(repr(V('1.0')), "NormalizedVersion('1.0')") + def test_basic_versions(self): for v, s in self.versions: @@ -44,6 +48,12 @@ for s in irrational: self.assertRaises(IrrationalVersionError, V, s) + def test_huge_version(self): + + self.assertEquals(str(V('1980.0')), '1980.0') + self.assertRaises(HugeMajorVersionNumError, V, '1981.0') + self.assertEquals(str(V('1981.0', error_on_huge_major_num=False)), '1981.0') + def test_comparison(self): r""" >>> V('1.2.0') == '1.2' @@ -51,12 +61,33 @@ ... TypeError: cannot compare NormalizedVersion and str + >>> V('1.2') < '1.3' + Traceback (most recent call last): + ... + TypeError: cannot compare NormalizedVersion and str + >>> V('1.2.0') == V('1.2') True >>> V('1.2.0') == V('1.2.3') False + >>> V('1.2.0') != V('1.2.3') + True >>> V('1.2.0') < V('1.2.3') True + >>> V('1.2.0') < V('1.2.0') + False + >>> V('1.2.0') <= V('1.2.0') + True + >>> V('1.2.0') <= V('1.2.3') + True + >>> V('1.2.3') <= V('1.2.0') + False + >>> V('1.2.0') >= V('1.2.0') + True + >>> V('1.2.3') >= V('1.2.0') + True + >>> V('1.2.0') >= V('1.2.3') + False >>> (V('1.0') > V('1.0b2')) True >>> (V('1.0') > V('1.0c2') > V('1.0c1') > V('1.0b2') > V('1.0b1') @@ -101,6 +132,7 @@ self.assertEqual(suggest('1.0c2'), '1.0c2') self.assertEqual(suggest('walla walla washington'), None) self.assertEqual(suggest('2.4c1'), '2.4c1') + self.assertEqual(suggest('v1.0'), '1.0') # from setuptools self.assertEqual(suggest('0.4a1.r10'), '0.4a1.post10') @@ -151,6 +183,8 @@ self.assertFalse(VersionPredicate('Hey (<=2.5)').match('2.6.0')) self.assertTrue(VersionPredicate('Hey (>=2.5)').match('2.5.1')) + self.assertRaises(ValueError, VersionPredicate, '') + # XXX need to silent the micro version in this case #assert not VersionPredicate('Ho (<3.0,!=2.6)').match('2.6.3') @@ -164,11 +198,21 @@ for version in other_versions: self.assertFalse(V(version).is_final) +class VersionWhiteBoxTestCase(unittest.TestCase): + + def test_parse_numdots(self): + # For code coverage completeness, as pad_zeros_length can't be set or + # influenced from the public interface + self.assertEquals(V('1.0')._parse_numdots('1.0', '1.0', + pad_zeros_length=3), + [1, 0, 0]) + def test_suite(): #README = os.path.join(os.path.dirname(__file__), 'README.txt') #suite = [doctest.DocFileSuite(README), unittest.makeSuite(VersionTestCase)] - suite = [unittest.makeSuite(VersionTestCase)] + suite = [unittest.makeSuite(VersionTestCase), + unittest.makeSuite(VersionWhiteBoxTestCase)] return unittest.TestSuite(suite) if __name__ == "__main__": diff --git a/src/distutils2/version.py b/src/distutils2/version.py --- a/src/distutils2/version.py +++ b/src/distutils2/version.py @@ -1,4 +1,3 @@ -import sys import re from distutils2.errors import IrrationalVersionError, HugeMajorVersionNumError @@ -379,8 +378,6 @@ def __init__(self, predicate): predicate = predicate.strip() match = _PLAIN_VERSIONS.match(predicate) - if match is None: - raise ValueError('Bad predicate "%s"' % predicate) self.name = None predicates = match.groups()[0] self.predicates = [_split_predicate(pred.strip()) @@ -391,8 +388,6 @@ def __init__(self, predicate): predicate = predicate.strip() match = _PLAIN_VERSIONS.match(predicate) - if match is None: - raise ValueError('Bad predicate "%s"' % predicate) self.name = None self.predicates = _split_predicate(match.groups()[0]) diff --git a/src/runtests-cov.py b/src/runtests-cov.py new file mode 100755 --- /dev/null +++ b/src/runtests-cov.py @@ -0,0 +1,119 @@ +#!/usr/bin/env python +"""Tests for distutils2. + +The tests for distutils2 are defined in the distutils2.tests package. +""" + +import sys +from os.path import dirname, islink, realpath +from optparse import OptionParser + +def ignore_prefixes(module): + """ Return a list of prefixes to ignore in the coverage report if + we want to completely skip `module`. + """ + # A function like that is needed because some GNU/Linux + # distributions, such a Ubuntu, really like to build link farm in + # /usr/lib in order to save a few bytes on the disk. + dirnames = [dirname(module.__file__)] + + pymod = module.__file__.rstrip("c") + if islink(pymod): + dirnames.append(dirname(realpath(pymod))) + return dirnames + +def parse_opts(): + parser = OptionParser(usage="%prog [OPTIONS]", + description="run the distutils2 unittests") + + parser.add_option("-q", "--quiet", help="do not print verbose messages", + action="store_true", default=False) + parser.add_option("-c", "--coverage", action="store_true", default=False, + help="produce a coverage report at the end of the run") + parser.add_option("-r", "--report", action="store_true", default=False, + help="produce a coverage report from the last test run") + parser.add_option("-m", "--show-missing", action="store_true", + default=False, + help=("Show line numbers of statements in each module " + "that weren't executed.")) + + opts, args = parser.parse_args() + return opts, args + +def coverage_report(opts): + import coverage + from distutils2.tests.support import unittest + cov = coverage.coverage() + cov.load() + + prefixes = ["runtests", "distutils2/tests", "distutils2/_backport"] + prefixes += ignore_prefixes(unittest) + + try: + import docutils + prefixes += ignore_prefixes(docutils) + except ImportError: + # that module is completely optional + pass + + try: + import roman + prefixes += ignore_prefixes(roman) + except ImportError: + # that module is also completely optional + pass + + cov.report(omit_prefixes=prefixes, show_missing=opts.show_missing) + + +def test_main(): + opts, args = parse_opts() + verbose = not opts.quiet + ret = 0 + + if opts.coverage or opts.report: + import coverage + + if opts.coverage: + cov = coverage.coverage() + cov.erase() + cov.start() + if not opts.report: + ret = run_tests(verbose) + if opts.coverage: + cov.stop() + cov.save() + + if opts.report or opts.coverage: + coverage_report(opts) + + return ret + +def run_tests(verbose): + import distutils2.tests + from distutils2.tests import run_unittest, reap_children, TestFailed + from distutils2._backport.tests import test_suite as btest_suite + # XXX just supporting -q right now to enable detailed/quiet output + if len(sys.argv) > 1: + verbose = sys.argv[-1] != '-q' + else: + verbose = 1 + try: + try: + run_unittest([distutils2.tests.test_suite(), btest_suite()], + verbose_=verbose) + return 0 + except TestFailed: + return 1 + finally: + reap_children() + +if __name__ == "__main__": + try: + from distutils2.tests.support import unittest + except ImportError: + sys.stderr.write('Error: You have to install unittest2') + sys.exit(1) + + sys.exit(test_main()) + diff --git a/src/setup.py b/src/setup.py --- a/src/setup.py +++ b/src/setup.py @@ -11,7 +11,6 @@ from distutils2.compiler.ccompiler import new_compiler from distutils2.command.sdist import sdist from distutils2.command.install import install -from distutils2 import __version__ as VERSION from distutils2.util import find_packages f = open('README.txt') -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:46 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:46 +0200 Subject: [Python-checkins] distutils2: Merge from Josip Message-ID: tarek.ziade pushed 0f543ed42707 to distutils2: http://hg.python.org/distutils2/rev/0f543ed42707 changeset: 443:0f543ed42707 parent: 441:593b7db7a5b4 parent: 442:74e5249b479f user: ?ric Araujo date: Thu Aug 05 16:23:54 2010 +0200 summary: Merge from Josip files: src/distutils2/command/install.py, src/distutils2/command/install_distinfo.py, src/distutils2/tests/test_install_distinfo.py diff --git a/src/distutils2/command/install.py b/src/distutils2/command/install.py --- a/src/distutils2/command/install.py +++ b/src/distutils2/command/install.py @@ -82,13 +82,10 @@ # .dist-info related arguments, read by install_dist_info ('no-distinfo', None, 'do not create a .dist-info directory'), - ('distinfo-dir=', None, - 'directory where the the .dist-info directory will ' - 'be installed'), ('installer=', None, 'the name of the installer'), ('requested', None, 'generate a REQUESTED file'), ('no-requested', None, 'do not generate a REQUESTED file'), - ('no-distinfo-record', None, 'do not generate a RECORD file'), + ('no-record', None, 'do not generate a RECORD file'), ] boolean_options = ['compile', 'force', 'skip-build', 'no-dist-info', @@ -173,10 +170,9 @@ # .dist-info related options self.no_distinfo = None - self.distinfo_dir = None self.installer = None self.requested = None - self.no_distinfo_record = None + self.no_record = None # -- Option finalizing methods ------------------------------------- diff --git a/src/distutils2/command/install_distinfo.py b/src/distutils2/command/install_distinfo.py --- a/src/distutils2/command/install_distinfo.py +++ b/src/distutils2/command/install_distinfo.py @@ -10,6 +10,8 @@ automatically by the ``install`` command. """ +# This file was created from the code for the former command install_egg_info + import os import csv import re @@ -34,12 +36,12 @@ ('installer=', None, 'the name of the installer'), ('requested', None, 'generate a REQUESTED file'), ('no-requested', None, 'do not generate a REQUESTED file'), - ('no-distinfo-record', None, 'do not generate a RECORD file'), + ('no-record', None, 'do not generate a RECORD file'), ] boolean_options = [ 'requested', - 'no-dist-record', + 'no-record', ] negative_opt = {'no-requested': 'requested'} @@ -48,15 +50,13 @@ self.distinfo_dir = None self.installer = None self.requested = None - self.no_distinfo_record = None + self.no_record = None def finalize_options(self): self.set_undefined_options('install', - ('distinfo_dir', 'distinfo_dir'), ('installer', 'installer'), ('requested', 'requested'), - ('no_distinfo_record', - 'no_distinfo_record')) + ('no_record', 'no_record')) self.set_undefined_options('install_lib', ('install_dir', 'distinfo_dir')) @@ -65,8 +65,8 @@ self.installer = 'distutils' if self.requested is None: self.requested = True - if self.no_distinfo_record is None: - self.no_distinfo_record = False + if self.no_record is None: + self.no_record = False metadata = self.distribution.metadata @@ -111,7 +111,7 @@ f.close() self.outputs.append(requested_path) - if not self.no_distinfo_record: + if not self.no_record: record_path = os.path.join(self.distinfo_dir, 'RECORD') log.info('Creating %s' % (record_path,)) f = open(record_path, 'wb') diff --git a/src/distutils2/tests/test_install_distinfo.py b/src/distutils2/tests/test_install_distinfo.py --- a/src/distutils2/tests/test_install_distinfo.py +++ b/src/distutils2/tests/test_install_distinfo.py @@ -123,7 +123,7 @@ cmd.initialize_options() cmd.distinfo_dir = install_dir - cmd.no_distinfo_record = True + cmd.no_record = True cmd.ensure_finalized() cmd.run() -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:46 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:46 +0200 Subject: [Python-checkins] distutils2: Don't use the mirrors in tests when not needed. Message-ID: tarek.ziade pushed 1d2f4bc24ad3 to distutils2: http://hg.python.org/distutils2/rev/1d2f4bc24ad3 changeset: 445:1d2f4bc24ad3 user: Alexis Metaireau date: Thu Jul 15 02:51:51 2010 +0200 summary: Don't use the mirrors in tests when not needed. files: src/distutils2/tests/test_pypi_simple.py diff --git a/src/distutils2/tests/test_pypi_simple.py b/src/distutils2/tests/test_pypi_simple.py --- a/src/distutils2/tests/test_pypi_simple.py +++ b/src/distutils2/tests/test_pypi_simple.py @@ -25,11 +25,13 @@ if hosts is None: hosts = (server.full_address.strip("http://"),) kwargs['hosts'] = hosts + if not 'mirrors' in kwargs: + kwargs['mirrors'] = [] # to speed up tests return simple.SimpleIndex(server.full_address + base_url, *args, **kwargs) def test_bad_urls(self): - index = simple.SimpleIndex() + index = simple.SimpleIndex(mirrors=[]) url = 'http://127.0.0.1:0/nonesuch/test_simple' try: v = index._open_url(url) @@ -41,7 +43,7 @@ # issue 16 # easy_install inquant.contentmirror.plone breaks because of a typo # in its home URL - index = simple.SimpleIndex(hosts=('www.example.com',)) + index = simple.SimpleIndex(hosts=('www.example.com',), mirrors=[]) url = 'url:%20https://svn.plone.org/svn/collective/inquant.contentmirror.plone/trunk' try: v = index._open_url(url) @@ -100,13 +102,13 @@ self.assertEqual("%s" % last_distribution.version, '2.0.1') def test_is_browsable(self): - index = simple.SimpleIndex(follow_externals=False) + index = simple.SimpleIndex(follow_externals=False, mirrors=[]) self.assertTrue(index._is_browsable(index.index_url + "test")) # Now, when following externals, we can have a list of hosts to trust. # and don't follow other external links than the one described here. index = simple.SimpleIndex(hosts=["pypi.python.org", "test.org"], - follow_externals=True) + follow_externals=True, mirrors=[]) good_urls = ( "http://pypi.python.org/foo/bar", "http://pypi.python.org/simple/foobar", @@ -126,13 +128,14 @@ self.assertFalse(index._is_browsable(url)) # allow all hosts - index = simple.SimpleIndex(follow_externals=True, hosts=("*",)) + index = simple.SimpleIndex(follow_externals=True, hosts=("*",), + mirrors=[]) self.assertTrue(index._is_browsable("http://an-external.link/path")) self.assertTrue(index._is_browsable("pypi.test.tld/a/path")) # specify a list of hosts we want to allow index = simple.SimpleIndex(follow_externals=True, - hosts=("*.test.tld",)) + hosts=("*.test.tld",), mirrors=[]) self.assertFalse(index._is_browsable("http://an-external.link/path")) self.assertTrue(index._is_browsable("http://pypi.test.tld/a/path")) @@ -176,7 +179,7 @@ index_url = server.full_address + '/simple/' # scan a test index - index = simple.SimpleIndex(index_url, follow_externals=True) + index = simple.SimpleIndex(index_url, follow_externals=True, mirrors=[]) dists = index.find("foobar") server.stop() @@ -230,7 +233,7 @@ def test_simple_link_matcher(self): # Test that the simple link matcher yields the right links""" - index = simple.SimpleIndex(follow_externals=False) + index = simple.SimpleIndex(follow_externals=False, mirrors=[]) # Here, we define: # 1. one link that must be followed, cause it's a download one -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:46 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:46 +0200 Subject: [Python-checkins] distutils2: Did some naming changes in install_distinfo Message-ID: tarek.ziade pushed 74e5249b479f to distutils2: http://hg.python.org/distutils2/rev/74e5249b479f changeset: 442:74e5249b479f parent: 433:aaf2faeb75e5 user: Josip Djolonga date: Wed Aug 04 18:45:36 2010 +0200 summary: Did some naming changes in install_distinfo files: src/distutils2/command/install.py, src/distutils2/command/install_distinfo.py, src/distutils2/tests/test_install_distinfo.py diff --git a/src/distutils2/command/install.py b/src/distutils2/command/install.py --- a/src/distutils2/command/install.py +++ b/src/distutils2/command/install.py @@ -82,13 +82,10 @@ # .dist-info related arguments, read by install_dist_info ('no-distinfo', None, 'do not create a .dist-info directory'), - ('distinfo-dir=', None, - 'directory where the the .dist-info directory will ' - 'be installed'), ('installer=', None, 'the name of the installer'), ('requested', None, 'generate a REQUESTED file'), ('no-requested', None, 'do not generate a REQUESTED file'), - ('no-distinfo-record', None, 'do not generate a RECORD file'), + ('no-record', None, 'do not generate a RECORD file'), ] boolean_options = ['compile', 'force', 'skip-build', 'no-dist-info', @@ -173,10 +170,9 @@ # .dist-info related options self.no_distinfo = None - self.distinfo_dir = None self.installer = None self.requested = None - self.no_distinfo_record = None + self.no_record = None # -- Option finalizing methods ------------------------------------- diff --git a/src/distutils2/command/install_distinfo.py b/src/distutils2/command/install_distinfo.py --- a/src/distutils2/command/install_distinfo.py +++ b/src/distutils2/command/install_distinfo.py @@ -10,6 +10,8 @@ automatically by the ``install`` command. """ +# This file was created from the code for the former command install_egg_info + from distutils2.command.cmd import Command from distutils2 import log from distutils2._backport.shutil import rmtree @@ -33,12 +35,12 @@ ('installer=', None, 'the name of the installer'), ('requested', None, 'generate a REQUESTED file'), ('no-requested', None, 'do not generate a REQUESTED file'), - ('no-distinfo-record', None, 'do not generate a RECORD file'), + ('no-record', None, 'do not generate a RECORD file'), ] boolean_options = [ 'requested', - 'no-dist-record', + 'no-record', ] negative_opt = {'no-requested': 'requested'} @@ -47,15 +49,13 @@ self.distinfo_dir = None self.installer = None self.requested = None - self.no_distinfo_record = None + self.no_record = None def finalize_options(self): self.set_undefined_options('install', - ('distinfo_dir', 'distinfo_dir'), ('installer', 'installer'), ('requested', 'requested'), - ('no_distinfo_record', - 'no_distinfo_record')) + ('no_record', 'no_record')) self.set_undefined_options('install_lib', ('install_dir', 'distinfo_dir')) @@ -64,8 +64,8 @@ self.installer = 'distutils' if self.requested is None: self.requested = True - if self.no_distinfo_record is None: - self.no_distinfo_record = False + if self.no_record is None: + self.no_record = False metadata = self.distribution.metadata @@ -110,7 +110,7 @@ f.close() self.outputs.append(requested_path) - if not self.no_distinfo_record: + if not self.no_record: record_path = os.path.join(self.distinfo_dir, 'RECORD') log.info('Creating %s' % (record_path,)) f = open(record_path, 'wb') diff --git a/src/distutils2/tests/test_install_distinfo.py b/src/distutils2/tests/test_install_distinfo.py --- a/src/distutils2/tests/test_install_distinfo.py +++ b/src/distutils2/tests/test_install_distinfo.py @@ -119,7 +119,7 @@ cmd.initialize_options() cmd.distinfo_dir = install_dir - cmd.no_distinfo_record = True + cmd.no_record = True cmd.ensure_finalized() cmd.run() -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:46 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:46 +0200 Subject: [Python-checkins] distutils2: Pass attribute keys instead of Metadata fields. Message-ID: tarek.ziade pushed 430c897bd635 to distutils2: http://hg.python.org/distutils2/rev/430c897bd635 changeset: 454:430c897bd635 user: Alexis Metaireau date: Wed Jul 21 18:54:58 2010 +0200 summary: Pass attribute keys instead of Metadata fields. files: src/distutils2/metadata.py diff --git a/src/distutils2/metadata.py b/src/distutils2/metadata.py --- a/src/distutils2/metadata.py +++ b/src/distutils2/metadata.py @@ -337,6 +337,9 @@ def update(self, other=None, **kwargs): """Set metadata values from the given mapping + + Convert the keys to Metadata fields. Given keys that don't match a + metadata argument will not be used. If overwrite is set to False, just add metadata values that are actually not defined. @@ -347,8 +350,8 @@ Empty values (e.g. None and []) are not setted this way. """ def _set(key, value): - if value not in ([], None): - self.set(key, value) + if value not in ([], None) and key in _ATTR2FIELD: + self.set(self._convert_name(key), value) if other is None: pass -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:46 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:46 +0200 Subject: [Python-checkins] distutils2: Make a warning if a distribution URL can't be parsed from the simple index. Message-ID: tarek.ziade pushed 9a71b51d7f90 to distutils2: http://hg.python.org/distutils2/rev/9a71b51d7f90 changeset: 453:9a71b51d7f90 user: Alexis Metaireau date: Wed Jul 21 16:52:26 2010 +0200 summary: Make a warning if a distribution URL can't be parsed from the simple index. files: src/distutils2/index/dist.py, src/distutils2/index/simple.py diff --git a/src/distutils2/index/dist.py b/src/distutils2/index/dist.py --- a/src/distutils2/index/dist.py +++ b/src/distutils2/index/dist.py @@ -418,9 +418,9 @@ version = archive_name.lstrip(name) else: name, version = eager_split(archive_name) - + version = suggest_normalized_version(version) - if version != "" and name != "": + if version is not None and name != "": return (name.lower(), version) else: raise CantParseArchiveName(archive_name) diff --git a/src/distutils2/index/simple.py b/src/distutils2/index/simple.py --- a/src/distutils2/index/simple.py +++ b/src/distutils2/index/simple.py @@ -11,12 +11,13 @@ import sys import urllib2 import urlparse +import logging from distutils2.index.base import IndexClient from distutils2.index.dist import (ReleasesList, EXTENSIONS, get_infos_from_url, MD5_HASH) from distutils2.index.errors import (IndexError, DownloadError, - UnableToDownload) + UnableToDownload, CantParseArchiveName) from distutils2.index.mirrors import get_mirrors from distutils2 import __version__ as __distutils2_version__ @@ -219,9 +220,14 @@ if self._is_distribution(link) or is_download: self._processed_urls.append(link) # it's a distribution, so create a dist object - infos = get_infos_from_url(link, project_name, - is_external=not self.index_url in url) - self._register_release(release_info=infos) + try: + infos = get_infos_from_url(link, project_name, + is_external=not self.index_url in url) + except CantParseArchiveName as e: + logging.warning("version has not been parsed: %s" + % e) + else: + self._register_release(release_info=infos) else: if self._is_browsable(link) and follow_links: self._process_url(link, project_name, -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:46 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:46 +0200 Subject: [Python-checkins] distutils2: Updated mirror support for indexes. Message-ID: tarek.ziade pushed 35ac4bcb4b04 to distutils2: http://hg.python.org/distutils2/rev/35ac4bcb4b04 changeset: 449:35ac4bcb4b04 user: Alexis Metaireau date: Tue Jul 20 18:13:05 2010 +0200 summary: Updated mirror support for indexes. files: src/distutils2/index/mirrors.py, src/distutils2/index/simple.py, src/distutils2/tests/test_index_simple.py diff --git a/src/distutils2/index/mirrors.py b/src/distutils2/index/mirrors.py new file mode 100644 --- /dev/null +++ b/src/distutils2/index/mirrors.py @@ -0,0 +1,55 @@ +"""Utilities related to the mirror infrastructure defined in PEP 381. +See http://www.python.org/dev/peps/pep-0381/ +""" + +from string import ascii_lowercase +import socket + +DEFAULT_MIRROR_URL = "last.pypi.python.org" + +def get_mirrors(hostname=None): + """Return the list of mirrors from the last record found on the DNS + entry:: + + >>> from distutils2.index.mirrors import get_mirrors + >>> get_mirrors() + ['a.pypi.python.org', 'b.pypi.python.org', 'c.pypi.python.org', + 'd.pypi.python.org'] + + """ + if hostname is None: + hostname = DEFAULT_MIRROR_URL + + # return the last mirror registered on PyPI. + try: + unused, aliaslist, ipaddr = socket.gethostbyname_ex(hostname) + except socket.gaierror: + return [] + if len(aliaslist) < 2: + return [] + index_adress = aliaslist[-1] + end_letter = index_adress.split(".", 1) + + # determine the list from the last one. + return ["%s.%s" % (s, end_letter[1]) for s in string_range(end_letter[0])] + +def string_range(last): + """Compute the range of string between "a" and last. + + This works for simple "a to z" lists, but also for "a to zz" lists. + """ + for k in range(len(last)): + for x in product(ascii_lowercase, repeat=k+1): + result = ''.join(x) + yield result + if result == last: + return + +def product(*args, **kwds): + pools = map(tuple, args) * kwds.get('repeat', 1) + result = [[]] + for pool in pools: + result = [x+[y] for x in result for y in pool] + for prod in result: + yield tuple(prod) + diff --git a/src/distutils2/index/simple.py b/src/distutils2/index/simple.py --- a/src/distutils2/index/simple.py +++ b/src/distutils2/index/simple.py @@ -17,11 +17,11 @@ get_infos_from_url) from distutils2.index.errors import (IndexError, DownloadError, UnableToDownload) +from distutils2.index.mirrors import get_mirrors from distutils2 import __version__ as __distutils2_version__ # -- Constants ----------------------------------------------- -DEFAULT_INDEX_URL = "http://pypi.python.org/simple/" -DEFAULT_MIRROR_URL = "mirrors.pypi.python.org" +DEFAULT_INDEX_URL = "http://a.pypi.python.org/simple/" DEFAULT_HOSTS = ("*",) SOCKET_TIMEOUT = 15 USER_AGENT = "Python-urllib/%s distutils2/%s" % ( @@ -61,6 +61,9 @@ class Crawler(IndexClient): """Provides useful tools to request the Python Package Index simple API. + You can specify both mirrors and mirrors_url, but mirrors_url will only be + used if mirrors is set to None. + :param index_url: the url of the simple index to search on. :param follow_externals: tell if following external links is needed or not. Default is False. @@ -74,28 +77,30 @@ pick up the last final version. :param mirrors_url: the url to look on for DNS records giving mirror adresses. - :param mirrors: a list of mirrors to check out if problems - occurs while working with the one given in "url" + :param mirrors: a list of mirrors (see PEP 381). :param timeout: time in seconds to consider a url has timeouted. + :param mirrors_max_tries": number of times to try requesting informations + on mirrors before switching. """ def __init__(self, index_url=DEFAULT_INDEX_URL, hosts=DEFAULT_HOSTS, follow_externals=False, prefer_final=False, - mirrors_url=DEFAULT_MIRROR_URL, mirrors=None, - timeout=SOCKET_TIMEOUT): + mirrors_url=None, mirrors=None, + timeout=SOCKET_TIMEOUT, mirrors_max_tries=0): self.follow_externals = follow_externals - + + # mirroring attributes. if not index_url.endswith("/"): index_url += "/" - self._index_urls = [index_url] # if no mirrors are defined, use the method described in PEP 381. if mirrors is None: - try: - mirrors = socket.gethostbyname_ex(mirrors_url)[-1] - except socket.gaierror: - mirrors = [] - self._index_urls.extend(mirrors) - self._current_index_url = 0 + mirrors = get_mirrors(mirrors_url) + self._mirrors = set(mirrors) + self._mirrors_used = set() + self.index_url = index_url + self._mirrors_max_tries = mirrors_max_tries + self._mirrors_tries = 0 + self._timeout = timeout self._prefer_final = prefer_final @@ -108,10 +113,6 @@ self._processed_urls = [] self._releases = {} - @property - def index_url(self): - return self._index_urls[self._current_index_url] - def _search_for_releases(self, requirements): """Search for distributions and return a ReleaseList object containing the results @@ -128,14 +129,19 @@ def _switch_to_next_mirror(self): """Switch to the next mirror (eg. point self.index_url to the next - url. + mirror url. + + Raise a KeyError if all mirrors have been tried. """ - # Internally, iter over the _index_url iterable, if we have read all - # of the available indexes, raise an exception. - if self._current_index_url < len(self._index_urls): - self._current_index_url = self._current_index_url + 1 - else: - raise UnableToDownload("All mirrors fails") + self._mirrors_used.add(self.index_url) + index_url = self._mirrors.pop() + if not ("http://" or "https://" or "file://") in index_url: + index_url = "http://%s" % index_url + + if not index_url.endswith("/simple"): + index_url = "%s/simple/" % index_url + + self.index_url = index_url def _is_browsable(self, url): """Tell if the given URL can be browsed or not. @@ -270,8 +276,13 @@ self._process_url(url, name) except DownloadError: # if an error occurs, try with the next index_url - # (provided by the mirrors) - self._switch_to_next_mirror() + if self._mirrors_tries >= self._mirrors_max_tries: + try: + self._switch_to_next_mirror() + except KeyError: + raise UnableToDownload("Tried all mirrors") + else: + self._mirrors_tries += 1 self._releases.clear() self._process_index_page(name) diff --git a/src/distutils2/tests/test_index_simple.py b/src/distutils2/tests/test_index_simple.py --- a/src/distutils2/tests/test_index_simple.py +++ b/src/distutils2/tests/test_index_simple.py @@ -222,7 +222,7 @@ # create the index using both servers crawler = Crawler(server.full_address + "/simple/", hosts=('*',), timeout=1, # set the timeout to 1s for the tests - mirrors=[mirror.full_address + "/simple/", ]) + mirrors=[mirror.full_address, ]) # this should not raise a timeout self.assertEqual(4, len(crawler.find("foo"))) -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:45 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:45 +0200 Subject: [Python-checkins] distutils2: merged with Tarek's code Message-ID: tarek.ziade pushed e6a99de6bb38 to distutils2: http://hg.python.org/distutils2/rev/e6a99de6bb38 changeset: 429:e6a99de6bb38 parent: 428:0f61b7399cfa parent: 380:feceafae5855 user: Josip Djolonga date: Sun Jul 18 03:08:42 2010 +0200 summary: merged with Tarek's code files: src/distutils2/_backport/pkgutil.py diff --git a/.hgignore b/.hgignore --- a/.hgignore +++ b/.hgignore @@ -1,5 +1,9 @@ -.*\.pyc$ -.*\.pyo$ -^src/build -^docs/build -.*\.swp$ +syntax: glob +*.py[co] +__pycache__/ +configure.cache +build/ +MANIFEST +dist/ +*.swp +.coverage \ No newline at end of file diff --git a/docs/design/pep-0376.txt b/docs/design/pep-0376.txt --- a/docs/design/pep-0376.txt +++ b/docs/design/pep-0376.txt @@ -402,7 +402,7 @@ ``.egg-info`` directory that contains a PKG-INFO that matches `name` for the `name` metadata. - Notice that there should be at most one result. The first result founded + Notice that there should be at most one result. The first result found is returned. If the directory is not found, returns None. - ``get_file_users(path)`` -> iterator of ``Distribution`` instances. @@ -633,7 +633,7 @@ Distributions installed using existing, pre-standardization formats do not have the necessary metadata available for the new API, and thus will be -ignored. Third-party tools may of course to continue to support previous +ignored. Third-party tools may of course continue to support previous formats in addition to the new format, in order to ease the transition. diff --git a/docs/design/wiki.rst b/docs/design/wiki.rst --- a/docs/design/wiki.rst +++ b/docs/design/wiki.rst @@ -282,7 +282,7 @@ mailman/etc/* = {config} # 8 mailman/foo/**/bar/*.cfg = {config}/baz # 9 mailman/foo/**/*.cfg = {config}/hmm # 9, 10 - some-new-semantic.txt = {funky-crazy-category} # 11 + some-new-semantic.sns = {funky-crazy-category} # 11 The glob definitions are relative paths that match files from the top of the source tree (the location of ``setup.cfg``). Forward slashes @@ -399,7 +399,7 @@ the folder hierarchy from the module until we find a setup.cfg? A setup.cfg is necessary if you use distutils2, is it not? - -> information founded in setup.cfg will be put in the *FILES* file upon + -> information found in setup.cfg will be put in the *FILES* file upon installation in the egg-info directory. IOW in the unbuit-egg case, we would need to create that dir, then use pkgutil APIs. diff --git a/docs/source/index.rst b/docs/source/index.rst --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -14,6 +14,10 @@ metadata pkgutil depgraph + new_commands + test_framework + pypi + version Indices and tables ================== diff --git a/docs/source/metadata.rst b/docs/source/metadata.rst --- a/docs/source/metadata.rst +++ b/docs/source/metadata.rst @@ -17,7 +17,7 @@ Reading metadata ================ -The :class:`DistributionMetadata` class can be instanciated with the path of +The :class:`DistributionMetadata` class can be instantiated with the path of the metadata file, and provides a dict-like interface to the values:: >>> from distutils2.metadata import DistributionMetadata @@ -32,14 +32,14 @@ ["pywin32; sys.platform == 'win32'", "Sphinx"] The fields that supports environment markers can be automatically ignored if -the object is instanciated using the ``platform_dependant`` option. +the object is instantiated using the ``platform_dependent`` option. :class:`DistributionMetadata` will interpret in the case the markers and will automatically remove the fields that are not compliant with the running environment. Here's an example under Mac OS X. The win32 dependency we saw earlier is ignored:: >>> from distutils2.metadata import DistributionMetadata - >>> metadata = DistributionMetadata('PKG-INFO', platform_dependant=True) + >>> metadata = DistributionMetadata('PKG-INFO', platform_dependent=True) >>> metadata['Requires-Dist'] ['bar'] @@ -53,7 +53,7 @@ >>> from distutils2.metadata import DistributionMetadata >>> context = {'sys.platform': 'win32'} - >>> metadata = DistributionMetadata('PKG-INFO', platform_dependant=True, + >>> metadata = DistributionMetadata('PKG-INFO', platform_dependent=True, ... execution_context=context) ... >>> metadata['Requires-Dist'] = ["pywin32; sys.platform == 'win32'", @@ -71,15 +71,15 @@ >>> metadata.write('/to/my/PKG-INFO') The class will pick the best version for the metadata, depending on the values -provided. If all the values provided exists in all versions, teh class will -used :attr:`metadata.PKG_INFO_PREFERRED_VERSION`. It is set by default to 1.0. +provided. If all the values provided exist in all versions, the class will +use :attr:`metadata.PKG_INFO_PREFERRED_VERSION`. It is set by default to 1.0. Conflict checking and best version ================================== Some fields in :pep:`345` have to follow a version scheme in their versions -predicate. When the scheme is violated, a warning is emited:: +predicate. When the scheme is violated, a warning is emitted:: >>> from distutils2.metadata import DistributionMetadata >>> metadata = DistributionMetadata() @@ -90,6 +90,3 @@ .. TODO talk about check() - - - diff --git a/docs/source/new_commands.rst b/docs/source/new_commands.rst new file mode 100644 --- /dev/null +++ b/docs/source/new_commands.rst @@ -0,0 +1,65 @@ +======== +Commands +======== + +Distutils2 provides a set of commands that are not present in distutils itself. +You might recognize some of them from other projects, like Distribute or +Setuptools. + +``upload_docs`` - Upload package documentation to PyPI +====================================================== + +PyPI now supports uploading project documentation to the dedicated URL +http://packages.python.org//. + +The ``upload_docs`` command will create the necessary zip file out of a +documentation directory and will post to the repository. + +Note that to upload the documentation of a project, the corresponding version +must already be registered with PyPI, using the distutils ``register`` +command -- just like the ``upload`` command. + +Assuming there is an ``Example`` project with documentation in the +subdirectory ``docs``, e.g.:: + + Example/ + |-- example.py + |-- setup.cfg + |-- setup.py + |-- docs + | |-- build + | | `-- html + | | | |-- index.html + | | | `-- tips_tricks.html + | |-- conf.py + | |-- index.txt + | `-- tips_tricks.txt + +You can simply pass the documentation directory path to the ``upload_docs`` +command:: + + python setup.py upload_docs --upload-dir=docs/build/html + +As with any other ``setuptools`` based command, you can define useful +defaults in the ``setup.cfg`` of your Python project, e.g.: + +.. code-block:: ini + + [upload_docs] + upload-dir = docs/build/html + +The ``upload_docs`` command has the following options: + +``--upload-dir`` + The directory to be uploaded to the repository. The default value is + ``docs`` in project root. + +``--show-response`` + Display the full response text from server; this is useful for debugging + PyPI problems. + +``--repository=URL, -r URL`` + The URL of the repository to upload to. Defaults to + http://pypi.python.org/pypi (i.e., the main PyPI installation). + + diff --git a/docs/source/pypi.rst b/docs/source/pypi.rst new file mode 100644 --- /dev/null +++ b/docs/source/pypi.rst @@ -0,0 +1,195 @@ +========================================= +Tools to query PyPI: the PyPI package +========================================= + +Distutils2 comes with a module (eg. `distutils2.pypi`) which contains +facilities to access the Python Package Index (named "pypi", and avalaible on +the url `http://pypi.python.org`. + +There is two ways to retrieve data from pypi: using the *simple* API, and using +*XML-RPC*. The first one is in fact a set of HTML pages avalaible at +`http://pypi.python.org/simple/`, and the second one contains a set of XML-RPC +methods. In order to reduce the overload caused by running distant methods on +the pypi server (by using the XML-RPC methods), the best way to retrieve +informations is by using the simple API, when it contains the information you +need. + +Distutils2 provides two python modules to ease the work with those two APIs: +`distutils2.pypi.simple` and `distutils2.pypi.xmlrpc`. Both of them depends on +another python module: `distutils2.pypi.dist`. + + +Requesting information via the "simple" API `distutils2.pypi.simple` +==================================================================== + +`distutils2.pypi.simple` can process the Python Package Index and return and +download urls of distributions, for specific versions or latests, but it also +can process external html pages, with the goal to find *pypi unhosted* versions +of python distributions. + +You should use `distutils2.pypi.simple` for: + + * Search distributions by name and versions. + * Process pypi external pages. + * Download distributions by name and versions. + +And should not be used to: + + * Things that will end up in too long index processing (like "finding all + distributions with a specific version, no matters the name") + +API +---- + +Here is a complete overview of the APIs of the SimpleIndex class. + +.. autoclass:: distutils2.pypi.simple.SimpleIndex + :members: + +Usage Exemples +--------------- + +To help you understand how using the `SimpleIndex` class, here are some basic +usages. + +Request PyPI to get a specific distribution +++++++++++++++++++++++++++++++++++++++++++++ + +Supposing you want to scan the PyPI index to get a list of distributions for +the "foobar" project. You can use the "find" method for that:: + + >>> from distutils2.pypi import SimpleIndex + >>> client = SimpleIndex() + >>> client.find("foobar") + [, ] + +Note that you also can request the client about specific versions, using version +specifiers (described in `PEP 345 +`_):: + + >>> client.find("foobar < 1.2") + [, ] + +`find` returns a list of distributions, but you also can get the last +distribution (the more up to date) that fullfil your requirements, like this:: + + >>> client.get("foobar < 1.2") + + +Download distributions ++++++++++++++++++++++++ + +As it can get the urls of distributions provided by PyPI, the `SimpleIndex` +client also can download the distributions and put it for you in a temporary +destination:: + + >>> client.download("foobar") + /tmp/temp_dir/foobar-1.2.tar.gz + +You also can specify the directory you want to download to:: + + >>> client.download("foobar", "/path/to/my/dir") + /path/to/my/dir/foobar-1.2.tar.gz + +While downloading, the md5 of the archive will be checked, if not matches, it +will try another time, then if fails again, raise `MD5HashDoesNotMatchError`. + +Internally, that's not the SimpleIndex which download the distributions, but the +`PyPIDistribution` class. Please refer to this documentation for more details. + +Following PyPI external links +++++++++++++++++++++++++++++++ + +The default behavior for distutils2 is to *not* follow the links provided +by HTML pages in the "simple index", to find distributions related +downloads. + +It's possible to tell the PyPIClient to follow external links by setting the +`follow_externals` attribute, on instanciation or after:: + + >>> client = SimpleIndex(follow_externals=True) + +or :: + + >>> client = SimpleIndex() + >>> client.follow_externals = True + +Working with external indexes, and mirrors ++++++++++++++++++++++++++++++++++++++++++++ + +The default `SimpleIndex` behavior is to rely on the Python Package index stored +on PyPI (http://pypi.python.org/simple). + +As you can need to work with a local index, or private indexes, you can specify +it using the index_url parameter:: + + >>> client = SimpleIndex(index_url="file://filesystem/path/") + +or :: + + >>> client = SimpleIndex(index_url="http://some.specific.url/") + +You also can specify mirrors to fallback on in case the first index_url you +provided doesnt respond, or not correctly. The default behavior for +`SimpleIndex` is to use the list provided by Python.org DNS records, as +described in the :pep:`381` about mirroring infrastructure. + +If you don't want to rely on these, you could specify the list of mirrors you +want to try by specifying the `mirrors` attribute. It's a simple iterable:: + + >>> mirrors = ["http://first.mirror","http://second.mirror"] + >>> client = SimpleIndex(mirrors=mirrors) + + +Requesting informations via XML-RPC (`distutils2.pypi.XmlRpcIndex`) +========================================================================== + +The other method to request the Python package index, is using the XML-RPC +methods. Distutils2 provides a simple wrapper around `xmlrpclib +`_, that can return you +`PyPIDistribution` objects. + +:: + >>> from distutils2.pypi import XmlRpcIndex() + >>> client = XmlRpcIndex() + + +PyPI Distributions +================== + +Both `SimpleIndex` and `XmlRpcIndex` classes works with the classes provided +in the `pypi.dist` package. + +`PyPIDistribution` +------------------ + +`PyPIDistribution` is a simple class that defines the following attributes: + +:name: + The name of the package. `foobar` in our exemples here +:version: + The version of the package +:location: + If the files from the archive has been downloaded, here is the path where + you can find them. +:url: + The url of the distribution + +.. autoclass:: distutils2.pypi.dist.PyPIDistribution + :members: + +`PyPIDistributions` +------------------- + +The `dist` module also provides another class, to work with lists of +`PyPIDistribution` classes. It allow to filter results and is used as a +container of + +.. autoclass:: distutils2.pypi.dist.PyPIDistributions + :members: + +At a higher level +================= + +XXX : A description about a wraper around PyPI simple and XmlRpc Indexes +(PyPIIndex ?) diff --git a/docs/source/test_framework.rst b/docs/source/test_framework.rst new file mode 100644 --- /dev/null +++ b/docs/source/test_framework.rst @@ -0,0 +1,88 @@ +============== +Test Framework +============== + +When you are testing code that works with distutils, you might find these tools +useful. + +``PyPIServer`` +============== + +PyPIServer is a class that implements an HTTP server running in a separate +thread. All it does is record the requests for further inspection. The recorded +data is available under ``requests`` attribute. The default +HTTP response can be overriden with the ``default_response_status``, +``default_response_headers`` and ``default_response_data`` attributes. + +By default, when accessing the server with urls beginning with `/simple/`, +the server also record your requests, but will look for files under +the `/tests/pypiserver/simple/` path. + +You can tell the sever to serve static files for other paths. This could be +accomplished by using the `static_uri_paths` parameter, as below:: + + server = PyPIServer(static_uri_paths=["first_path", "second_path"]) + +You need to create the content that will be served under the +`/tests/pypiserver/default` path. If you want to serve content from another +place, you also can specify another filesystem path (wich need to be under +`tests/pypiserver/`. This will replace the default behavior of the server, and +it will not serve content from the `default` dir :: + + server = PyPIServer(static_filesystem_paths=["path/to/your/dir"]) + +If you just need to add some paths to the existing ones, you can do as shown, +keeping in mind that the server will alwas try to load paths in reverse order +(e.g here, try "another/super/path" then the default one) :: + + server = PyPIServer(test_static_path="another/super/path") + server = PyPIServer("another/super/path") + # or + server.static_filesystem_paths.append("another/super/path") + +As a result of what, in your tests, while you need to use the PyPIServer, in +order to isolates the test cases, the best practice is to place the common files +in the `default` folder, and to create a directory for each specific test case:: + + server = PyPIServer(static_filesystem_paths = ["default", "test_pypi_server"], + static_uri_paths=["simple", "external"]) + +``PyPIServerTestCase`` +====================== + +``PyPIServerTestCase`` is a test case class with setUp and tearDown methods that +take care of a single PyPIServer instance attached as a ``pypi`` attribute on +the test class. Use it as one of the base classes in your test case:: + + class UploadTestCase(PyPIServerTestCase): + def test_something(self): + cmd = self.prepare_command() + cmd.ensure_finalized() + cmd.repository = self.pypi.full_address + cmd.run() + + environ, request_data = self.pypi.requests[-1] + self.assertEqual(request_data, EXPECTED_REQUEST_DATA) + +The ``use_pypi_server`` decorator +================================= + +You also can use a decorator for your tests, if you do not need the same server +instance along all you test case. So, you can specify, for each test method, +some initialisation parameters for the server. + +For this, you need to add a `server` parameter to your method, like this:: + + class SampleTestCase(TestCase): + @use_pypi_server() + def test_somthing(self, server): + # your tests goes here + +The decorator will instanciate the server for you, and run and stop it just +before and after your method call. You also can pass the server initializer, +just like this:: + + class SampleTestCase(TestCase): + @use_pypi_server("test_case_name") + def test_something(self, server): + # something diff --git a/docs/source/version.rst b/docs/source/version.rst new file mode 100644 --- /dev/null +++ b/docs/source/version.rst @@ -0,0 +1,64 @@ +====================== +Working with versions +====================== + +Distutils2 ships with a python package capable to work with version numbers. +It's an implementation of version specifiers `as defined in PEP 345 +`_ about +Metadata. + +`distutils2.version.NormalizedVersion` +====================================== + +A Normalized version corresponds to a specific version of a distribution, as +described in the PEP 345. So, you can work with the `NormalizedVersion` like +this:: + + >>> NormalizedVersion("1.2b1") + NormalizedVersion('1.2b1') + +If you try to use irrational version specifiers, an `IrrationalVersionError` +will be raised:: + + >>> NormalizedVersion("irrational_version_number") + ... + IrrationalVersionError: irrational_version_number + +You can compare NormalizedVersion objects, like this:: + + >>> NormalizedVersion("1.2b1") < NormalizedVersion("1.2") + True + +NormalizedVersion is used internally by `VersionPredicate` to do his stuff. + +`distutils2.version.suggest_normalized_version` +----------------------------------------------- + +You also can let the normalized version be suggested to you, using the +`suggest_normalized_version` function:: + + >>> suggest_normalized_version('2.1-rc1') + 2.1c1 + +If `suggest_normalized_version` can't actually suggest you a version, it will +return `None`:: + + >>> print suggest_normalized_version('not a version') + None + +`distutils2.version.VersionPredicate` +===================================== + +`VersionPredicate` knows how to parse stuff like "ProjectName (>=version)", the +class also provides a `match` method to test if a version number is the version +predicate:: + + >>> version = VersionPredicate("ProjectName (<1.2,>1.0") + >>> version.match("1.2.1") + False + >>> version.match("1.1.1") + True + +`is_valid_predicate` +-------------------- + diff --git a/src/CONTRIBUTORS.txt b/src/CONTRIBUTORS.txt --- a/src/CONTRIBUTORS.txt +++ b/src/CONTRIBUTORS.txt @@ -5,7 +5,7 @@ Distutils2 is a project that was started and that is maintained by Tarek Ziad??, and many people are contributing to the project. -If you did, please add your name below in alphabetical order ! +If you did, please add your name below in alphabetical order! Thanks to: @@ -13,14 +13,18 @@ - Pior Bastida - Titus Brown - Nicolas Cadou +- Konrad Delong - Josip Djolonga - Yannick Gringas +- Jeremy Kloth +- Martin von L??wis - Carl Meyer +- Alexis M??taireau +- Zubin Mithra - Michael Mulich -- George Peris +- George Peristerakis - Sean Reifschneider +- Luis Rojas - Erik Rose - Brian Rosner - Alexandre Vassalotti -- Martin von L??wis - diff --git a/src/DEVNOTES.txt b/src/DEVNOTES.txt --- a/src/DEVNOTES.txt +++ b/src/DEVNOTES.txt @@ -3,8 +3,8 @@ - Distutils2 runs from 2.4 to 3.2 (3.x not implemented yet), so make sure you don't use a syntax that doesn't work under - a specific Python version. + one of these Python versions. - Always run tests.sh before you push a change. This implies - that you have all Python versions installed. + that you have all Python versions installed from 2.4 to 2.6. diff --git a/src/Modules/_hashopenssl.c b/src/Modules/_hashopenssl.c new file mode 100644 --- /dev/null +++ b/src/Modules/_hashopenssl.c @@ -0,0 +1,524 @@ +/* Module that wraps all OpenSSL hash algorithms */ + +/* + * Copyright (C) 2005 Gregory P. Smith (greg at krypto.org) + * Licensed to PSF under a Contributor Agreement. + * + * Derived from a skeleton of shamodule.c containing work performed by: + * + * Andrew Kuchling (amk at amk.ca) + * Greg Stein (gstein at lyra.org) + * + */ + +#define PY_SSIZE_T_CLEAN + +#include "Python.h" +#include "structmember.h" + +#if (PY_VERSION_HEX < 0x02050000) +#define Py_ssize_t int +#endif + +/* EVP is the preferred interface to hashing in OpenSSL */ +#include + +#define MUNCH_SIZE INT_MAX + + +#ifndef HASH_OBJ_CONSTRUCTOR +#define HASH_OBJ_CONSTRUCTOR 0 +#endif + +typedef struct { + PyObject_HEAD + PyObject *name; /* name of this hash algorithm */ + EVP_MD_CTX ctx; /* OpenSSL message digest context */ +} EVPobject; + + +static PyTypeObject EVPtype; + + +#define DEFINE_CONSTS_FOR_NEW(Name) \ + static PyObject *CONST_ ## Name ## _name_obj; \ + static EVP_MD_CTX CONST_new_ ## Name ## _ctx; \ + static EVP_MD_CTX *CONST_new_ ## Name ## _ctx_p = NULL; + +DEFINE_CONSTS_FOR_NEW(md5) +DEFINE_CONSTS_FOR_NEW(sha1) +DEFINE_CONSTS_FOR_NEW(sha224) +DEFINE_CONSTS_FOR_NEW(sha256) +DEFINE_CONSTS_FOR_NEW(sha384) +DEFINE_CONSTS_FOR_NEW(sha512) + + +static EVPobject * +newEVPobject(PyObject *name) +{ + EVPobject *retval = (EVPobject *)PyObject_New(EVPobject, &EVPtype); + + /* save the name for .name to return */ + if (retval != NULL) { + Py_INCREF(name); + retval->name = name; + } + + return retval; +} + +/* Internal methods for a hash object */ + +static void +EVP_dealloc(PyObject *ptr) +{ + EVP_MD_CTX_cleanup(&((EVPobject *)ptr)->ctx); + Py_XDECREF(((EVPobject *)ptr)->name); + PyObject_Del(ptr); +} + + +/* External methods for a hash object */ + +PyDoc_STRVAR(EVP_copy__doc__, "Return a copy of the hash object."); + +static PyObject * +EVP_copy(EVPobject *self, PyObject *unused) +{ + EVPobject *newobj; + + if ( (newobj = newEVPobject(self->name))==NULL) + return NULL; + + EVP_MD_CTX_copy(&newobj->ctx, &self->ctx); + return (PyObject *)newobj; +} + +PyDoc_STRVAR(EVP_digest__doc__, +"Return the digest value as a string of binary data."); + +static PyObject * +EVP_digest(EVPobject *self, PyObject *unused) +{ + unsigned char digest[EVP_MAX_MD_SIZE]; + EVP_MD_CTX temp_ctx; + PyObject *retval; + unsigned int digest_size; + + EVP_MD_CTX_copy(&temp_ctx, &self->ctx); + digest_size = EVP_MD_CTX_size(&temp_ctx); + EVP_DigestFinal(&temp_ctx, digest, NULL); + + retval = PyString_FromStringAndSize((const char *)digest, digest_size); + EVP_MD_CTX_cleanup(&temp_ctx); + return retval; +} + +PyDoc_STRVAR(EVP_hexdigest__doc__, +"Return the digest value as a string of hexadecimal digits."); + +static PyObject * +EVP_hexdigest(EVPobject *self, PyObject *unused) +{ + unsigned char digest[EVP_MAX_MD_SIZE]; + EVP_MD_CTX temp_ctx; + PyObject *retval; + char *hex_digest; + unsigned int i, j, digest_size; + + /* Get the raw (binary) digest value */ + EVP_MD_CTX_copy(&temp_ctx, &self->ctx); + digest_size = EVP_MD_CTX_size(&temp_ctx); + EVP_DigestFinal(&temp_ctx, digest, NULL); + + EVP_MD_CTX_cleanup(&temp_ctx); + + /* Create a new string */ + /* NOTE: not thread safe! modifying an already created string object */ + /* (not a problem because we hold the GIL by default) */ + retval = PyString_FromStringAndSize(NULL, digest_size * 2); + if (!retval) + return NULL; + hex_digest = PyString_AsString(retval); + if (!hex_digest) { + Py_DECREF(retval); + return NULL; + } + + /* Make hex version of the digest */ + for(i=j=0; i> 4) & 0xf; + c = (c>9) ? c+'a'-10 : c + '0'; + hex_digest[j++] = c; + c = (digest[i] & 0xf); + c = (c>9) ? c+'a'-10 : c + '0'; + hex_digest[j++] = c; + } + return retval; +} + +PyDoc_STRVAR(EVP_update__doc__, +"Update this hash object's state with the provided string."); + +static PyObject * +EVP_update(EVPobject *self, PyObject *args) +{ + unsigned char *cp; + Py_ssize_t len; + + if (!PyArg_ParseTuple(args, "s#:update", &cp, &len)) + return NULL; + + if (len > 0 && len <= MUNCH_SIZE) { + EVP_DigestUpdate(&self->ctx, cp, Py_SAFE_DOWNCAST(len, Py_ssize_t, + unsigned int)); + } else { + Py_ssize_t offset = 0; + while (len) { + unsigned int process = len > MUNCH_SIZE ? MUNCH_SIZE : len; + EVP_DigestUpdate(&self->ctx, cp + offset, process); + len -= process; + offset += process; + } + } + Py_INCREF(Py_None); + return Py_None; +} + +static PyMethodDef EVP_methods[] = { + {"update", (PyCFunction)EVP_update, METH_VARARGS, EVP_update__doc__}, + {"digest", (PyCFunction)EVP_digest, METH_NOARGS, EVP_digest__doc__}, + {"hexdigest", (PyCFunction)EVP_hexdigest, METH_NOARGS, EVP_hexdigest__doc__}, + {"copy", (PyCFunction)EVP_copy, METH_NOARGS, EVP_copy__doc__}, + {NULL, NULL} /* sentinel */ +}; + +static PyObject * +EVP_get_block_size(EVPobject *self, void *closure) +{ + return PyInt_FromLong(EVP_MD_CTX_block_size(&((EVPobject *)self)->ctx)); +} + +static PyObject * +EVP_get_digest_size(EVPobject *self, void *closure) +{ + return PyInt_FromLong(EVP_MD_CTX_size(&((EVPobject *)self)->ctx)); +} + +static PyMemberDef EVP_members[] = { + {"name", T_OBJECT, offsetof(EVPobject, name), READONLY, PyDoc_STR("algorithm name.")}, + {NULL} /* Sentinel */ +}; + +static PyGetSetDef EVP_getseters[] = { + {"digest_size", + (getter)EVP_get_digest_size, NULL, + NULL, + NULL}, + {"block_size", + (getter)EVP_get_block_size, NULL, + NULL, + NULL}, + /* the old md5 and sha modules support 'digest_size' as in PEP 247. + * the old sha module also supported 'digestsize'. ugh. */ + {"digestsize", + (getter)EVP_get_digest_size, NULL, + NULL, + NULL}, + {NULL} /* Sentinel */ +}; + + +static PyObject * +EVP_repr(PyObject *self) +{ + char buf[100]; + PyOS_snprintf(buf, sizeof(buf), "<%s HASH object @ %p>", + PyString_AsString(((EVPobject *)self)->name), self); + return PyString_FromString(buf); +} + +#if HASH_OBJ_CONSTRUCTOR +static int +EVP_tp_init(EVPobject *self, PyObject *args, PyObject *kwds) +{ + static char *kwlist[] = {"name", "string", NULL}; + PyObject *name_obj = NULL; + char *nameStr; + unsigned char *cp = NULL; + Py_ssize_t len = 0; + const EVP_MD *digest; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|s#:HASH", kwlist, + &name_obj, &cp, &len)) { + return -1; + } + + if (!PyArg_Parse(name_obj, "s", &nameStr)) { + PyErr_SetString(PyExc_TypeError, "name must be a string"); + return -1; + } + + digest = EVP_get_digestbyname(nameStr); + if (!digest) { + PyErr_SetString(PyExc_ValueError, "unknown hash function"); + return -1; + } + EVP_DigestInit(&self->ctx, digest); + + self->name = name_obj; + Py_INCREF(self->name); + + if (cp && len) { + if (len > 0 && len <= MUNCH_SIZE) { + EVP_DigestUpdate(&self->ctx, cp, Py_SAFE_DOWNCAST(len, Py_ssize_t, + unsigned int)); + } else { + Py_ssize_t offset = 0; + while (len) { + unsigned int process = len > MUNCH_SIZE ? MUNCH_SIZE : len; + EVP_DigestUpdate(&self->ctx, cp + offset, process); + len -= process; + offset += process; + } + } + } + + return 0; +} +#endif + + +PyDoc_STRVAR(hashtype_doc, +"A hash represents the object used to calculate a checksum of a\n\ +string of information.\n\ +\n\ +Methods:\n\ +\n\ +update() -- updates the current digest with an additional string\n\ +digest() -- return the current digest value\n\ +hexdigest() -- return the current digest as a string of hexadecimal digits\n\ +copy() -- return a copy of the current hash object\n\ +\n\ +Attributes:\n\ +\n\ +name -- the hash algorithm being used by this object\n\ +digest_size -- number of bytes in this hashes output\n"); + +static PyTypeObject EVPtype = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "_hashlib.HASH", /*tp_name*/ + sizeof(EVPobject), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + /* methods */ + EVP_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + EVP_repr, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ + hashtype_doc, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + EVP_methods, /* tp_methods */ + EVP_members, /* tp_members */ + EVP_getseters, /* tp_getset */ +#if 1 + 0, /* tp_base */ + 0, /* tp_dict */ + 0, /* tp_descr_get */ + 0, /* tp_descr_set */ + 0, /* tp_dictoffset */ +#endif +#if HASH_OBJ_CONSTRUCTOR + (initproc)EVP_tp_init, /* tp_init */ +#endif +}; + +static PyObject * +EVPnew(PyObject *name_obj, + const EVP_MD *digest, const EVP_MD_CTX *initial_ctx, + const unsigned char *cp, Py_ssize_t len) +{ + EVPobject *self; + + if (!digest && !initial_ctx) { + PyErr_SetString(PyExc_ValueError, "unsupported hash type"); + return NULL; + } + + if ((self = newEVPobject(name_obj)) == NULL) + return NULL; + + if (initial_ctx) { + EVP_MD_CTX_copy(&self->ctx, initial_ctx); + } else { + EVP_DigestInit(&self->ctx, digest); + } + + if (cp && len) { + if (len > 0 && len <= MUNCH_SIZE) { + EVP_DigestUpdate(&self->ctx, cp, Py_SAFE_DOWNCAST(len, Py_ssize_t, + unsigned int)); + } else { + Py_ssize_t offset = 0; + while (len) { + unsigned int process = len > MUNCH_SIZE ? MUNCH_SIZE : len; + EVP_DigestUpdate(&self->ctx, cp + offset, process); + len -= process; + offset += process; + } + } + } + + return (PyObject *)self; +} + + +/* The module-level function: new() */ + +PyDoc_STRVAR(EVP_new__doc__, +"Return a new hash object using the named algorithm.\n\ +An optional string argument may be provided and will be\n\ +automatically hashed.\n\ +\n\ +The MD5 and SHA1 algorithms are always supported.\n"); + +static PyObject * +EVP_new(PyObject *self, PyObject *args, PyObject *kwdict) +{ + static char *kwlist[] = {"name", "string", NULL}; + PyObject *name_obj = NULL; + char *name; + const EVP_MD *digest; + unsigned char *cp = NULL; + Py_ssize_t len = 0; + + if (!PyArg_ParseTupleAndKeywords(args, kwdict, "O|s#:new", kwlist, + &name_obj, &cp, &len)) { + return NULL; + } + + if (!PyArg_Parse(name_obj, "s", &name)) { + PyErr_SetString(PyExc_TypeError, "name must be a string"); + return NULL; + } + + digest = EVP_get_digestbyname(name); + + return EVPnew(name_obj, digest, NULL, cp, len); +} + +/* + * This macro generates constructor function definitions for specific + * hash algorithms. These constructors are much faster than calling + * the generic one passing it a python string and are noticably + * faster than calling a python new() wrapper. Thats important for + * code that wants to make hashes of a bunch of small strings. + */ +#define GEN_CONSTRUCTOR(NAME) \ + static PyObject * \ + EVP_new_ ## NAME (PyObject *self, PyObject *args) \ + { \ + unsigned char *cp = NULL; \ + Py_ssize_t len = 0; \ + \ + if (!PyArg_ParseTuple(args, "|s#:" #NAME , &cp, &len)) { \ + return NULL; \ + } \ + \ + return EVPnew( \ + CONST_ ## NAME ## _name_obj, \ + NULL, \ + CONST_new_ ## NAME ## _ctx_p, \ + cp, len); \ + } + +/* a PyMethodDef structure for the constructor */ +#define CONSTRUCTOR_METH_DEF(NAME) \ + {"openssl_" #NAME, (PyCFunction)EVP_new_ ## NAME, METH_VARARGS, \ + PyDoc_STR("Returns a " #NAME \ + " hash object; optionally initialized with a string") \ + } + +/* used in the init function to setup a constructor */ +#define INIT_CONSTRUCTOR_CONSTANTS(NAME) do { \ + CONST_ ## NAME ## _name_obj = PyString_FromString(#NAME); \ + if (EVP_get_digestbyname(#NAME)) { \ + CONST_new_ ## NAME ## _ctx_p = &CONST_new_ ## NAME ## _ctx; \ + EVP_DigestInit(CONST_new_ ## NAME ## _ctx_p, EVP_get_digestbyname(#NAME)); \ + } \ +} while (0); + +GEN_CONSTRUCTOR(md5) +GEN_CONSTRUCTOR(sha1) +GEN_CONSTRUCTOR(sha224) +GEN_CONSTRUCTOR(sha256) +GEN_CONSTRUCTOR(sha384) +GEN_CONSTRUCTOR(sha512) + +/* List of functions exported by this module */ + +static struct PyMethodDef EVP_functions[] = { + {"new", (PyCFunction)EVP_new, METH_VARARGS|METH_KEYWORDS, EVP_new__doc__}, + CONSTRUCTOR_METH_DEF(md5), + CONSTRUCTOR_METH_DEF(sha1), + CONSTRUCTOR_METH_DEF(sha224), + CONSTRUCTOR_METH_DEF(sha256), + CONSTRUCTOR_METH_DEF(sha384), + CONSTRUCTOR_METH_DEF(sha512), + {NULL, NULL} /* Sentinel */ +}; + + +/* Initialize this module. */ + +PyMODINIT_FUNC +init_hashlib(void) +{ + PyObject *m; + + OpenSSL_add_all_digests(); + + /* TODO build EVP_functions openssl_* entries dynamically based + * on what hashes are supported rather than listing many + * but having some be unsupported. Only init appropriate + * constants. */ + + EVPtype.ob_type = &PyType_Type; + if (PyType_Ready(&EVPtype) < 0) + return; + + m = Py_InitModule("_hashlib", EVP_functions); + if (m == NULL) + return; + +#if HASH_OBJ_CONSTRUCTOR + Py_INCREF(&EVPtype); + PyModule_AddObject(m, "HASH", (PyObject *)&EVPtype); +#endif + + /* these constants are used by the convenience constructors */ + INIT_CONSTRUCTOR_CONSTANTS(md5); + INIT_CONSTRUCTOR_CONSTANTS(sha1); + INIT_CONSTRUCTOR_CONSTANTS(sha224); + INIT_CONSTRUCTOR_CONSTANTS(sha256); + INIT_CONSTRUCTOR_CONSTANTS(sha384); + INIT_CONSTRUCTOR_CONSTANTS(sha512); +} diff --git a/src/Modules/md5.c b/src/Modules/md5.c new file mode 100644 --- /dev/null +++ b/src/Modules/md5.c @@ -0,0 +1,381 @@ +/* + Copyright (C) 1999, 2000, 2002 Aladdin Enterprises. All rights reserved. + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + L. Peter Deutsch + ghost at aladdin.com + + */ +/* $Id: md5.c,v 1.6 2002/04/13 19:20:28 lpd Exp $ */ +/* + Independent implementation of MD5 (RFC 1321). + + This code implements the MD5 Algorithm defined in RFC 1321, whose + text is available at + http://www.ietf.org/rfc/rfc1321.txt + The code is derived from the text of the RFC, including the test suite + (section A.5) but excluding the rest of Appendix A. It does not include + any code or documentation that is identified in the RFC as being + copyrighted. + + The original and principal author of md5.c is L. Peter Deutsch + . Other authors are noted in the change history + that follows (in reverse chronological order): + + 2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order + either statically or dynamically; added missing #include + in library. + 2002-03-11 lpd Corrected argument list for main(), and added int return + type, in test program and T value program. + 2002-02-21 lpd Added missing #include in test program. + 2000-07-03 lpd Patched to eliminate warnings about "constant is + unsigned in ANSI C, signed in traditional"; made test program + self-checking. + 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. + 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5). + 1999-05-03 lpd Original version. + */ + +#include "md5.h" +#include + +#undef BYTE_ORDER /* 1 = big-endian, -1 = little-endian, 0 = unknown */ +#ifdef ARCH_IS_BIG_ENDIAN +# define BYTE_ORDER (ARCH_IS_BIG_ENDIAN ? 1 : -1) +#else +# define BYTE_ORDER 0 +#endif + +#define T_MASK ((md5_word_t)~0) +#define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87) +#define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9) +#define T3 0x242070db +#define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111) +#define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050) +#define T6 0x4787c62a +#define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec) +#define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe) +#define T9 0x698098d8 +#define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850) +#define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e) +#define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841) +#define T13 0x6b901122 +#define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c) +#define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71) +#define T16 0x49b40821 +#define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d) +#define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf) +#define T19 0x265e5a51 +#define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855) +#define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2) +#define T22 0x02441453 +#define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e) +#define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437) +#define T25 0x21e1cde6 +#define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829) +#define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278) +#define T28 0x455a14ed +#define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa) +#define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07) +#define T31 0x676f02d9 +#define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375) +#define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd) +#define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e) +#define T35 0x6d9d6122 +#define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3) +#define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb) +#define T38 0x4bdecfa9 +#define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f) +#define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f) +#define T41 0x289b7ec6 +#define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805) +#define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a) +#define T44 0x04881d05 +#define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6) +#define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a) +#define T47 0x1fa27cf8 +#define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a) +#define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb) +#define T50 0x432aff97 +#define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58) +#define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6) +#define T53 0x655b59c3 +#define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d) +#define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82) +#define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e) +#define T57 0x6fa87e4f +#define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f) +#define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb) +#define T60 0x4e0811a1 +#define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d) +#define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca) +#define T63 0x2ad7d2bb +#define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e) + + +static void +md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/) +{ + md5_word_t + a = pms->abcd[0], b = pms->abcd[1], + c = pms->abcd[2], d = pms->abcd[3]; + md5_word_t t; +#if BYTE_ORDER > 0 + /* Define storage only for big-endian CPUs. */ + md5_word_t X[16]; +#else + /* Define storage for little-endian or both types of CPUs. */ + md5_word_t xbuf[16]; + const md5_word_t *X; +#endif + + { +#if BYTE_ORDER == 0 + /* + * Determine dynamically whether this is a big-endian or + * little-endian machine, since we can use a more efficient + * algorithm on the latter. + */ + static const int w = 1; + + if (*((const md5_byte_t *)&w)) /* dynamic little-endian */ +#endif +#if BYTE_ORDER <= 0 /* little-endian */ + { + /* + * On little-endian machines, we can process properly aligned + * data without copying it. + */ + if (!((data - (const md5_byte_t *)0) & 3)) { + /* data are properly aligned */ + X = (const md5_word_t *)data; + } else { + /* not aligned */ + memcpy(xbuf, data, 64); + X = xbuf; + } + } +#endif +#if BYTE_ORDER == 0 + else /* dynamic big-endian */ +#endif +#if BYTE_ORDER >= 0 /* big-endian */ + { + /* + * On big-endian machines, we must arrange the bytes in the + * right order. + */ + const md5_byte_t *xp = data; + int i; + +# if BYTE_ORDER == 0 + X = xbuf; /* (dynamic only) */ +# else +# define xbuf X /* (static only) */ +# endif + for (i = 0; i < 16; ++i, xp += 4) + xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24); + } +#endif + } + +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) + + /* Round 1. */ + /* Let [abcd k s i] denote the operation + a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */ +#define F(x, y, z) (((x) & (y)) | (~(x) & (z))) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + F(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 0, 7, T1); + SET(d, a, b, c, 1, 12, T2); + SET(c, d, a, b, 2, 17, T3); + SET(b, c, d, a, 3, 22, T4); + SET(a, b, c, d, 4, 7, T5); + SET(d, a, b, c, 5, 12, T6); + SET(c, d, a, b, 6, 17, T7); + SET(b, c, d, a, 7, 22, T8); + SET(a, b, c, d, 8, 7, T9); + SET(d, a, b, c, 9, 12, T10); + SET(c, d, a, b, 10, 17, T11); + SET(b, c, d, a, 11, 22, T12); + SET(a, b, c, d, 12, 7, T13); + SET(d, a, b, c, 13, 12, T14); + SET(c, d, a, b, 14, 17, T15); + SET(b, c, d, a, 15, 22, T16); +#undef SET + + /* Round 2. */ + /* Let [abcd k s i] denote the operation + a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */ +#define G(x, y, z) (((x) & (z)) | ((y) & ~(z))) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + G(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 1, 5, T17); + SET(d, a, b, c, 6, 9, T18); + SET(c, d, a, b, 11, 14, T19); + SET(b, c, d, a, 0, 20, T20); + SET(a, b, c, d, 5, 5, T21); + SET(d, a, b, c, 10, 9, T22); + SET(c, d, a, b, 15, 14, T23); + SET(b, c, d, a, 4, 20, T24); + SET(a, b, c, d, 9, 5, T25); + SET(d, a, b, c, 14, 9, T26); + SET(c, d, a, b, 3, 14, T27); + SET(b, c, d, a, 8, 20, T28); + SET(a, b, c, d, 13, 5, T29); + SET(d, a, b, c, 2, 9, T30); + SET(c, d, a, b, 7, 14, T31); + SET(b, c, d, a, 12, 20, T32); +#undef SET + + /* Round 3. */ + /* Let [abcd k s t] denote the operation + a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */ +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + H(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 5, 4, T33); + SET(d, a, b, c, 8, 11, T34); + SET(c, d, a, b, 11, 16, T35); + SET(b, c, d, a, 14, 23, T36); + SET(a, b, c, d, 1, 4, T37); + SET(d, a, b, c, 4, 11, T38); + SET(c, d, a, b, 7, 16, T39); + SET(b, c, d, a, 10, 23, T40); + SET(a, b, c, d, 13, 4, T41); + SET(d, a, b, c, 0, 11, T42); + SET(c, d, a, b, 3, 16, T43); + SET(b, c, d, a, 6, 23, T44); + SET(a, b, c, d, 9, 4, T45); + SET(d, a, b, c, 12, 11, T46); + SET(c, d, a, b, 15, 16, T47); + SET(b, c, d, a, 2, 23, T48); +#undef SET + + /* Round 4. */ + /* Let [abcd k s t] denote the operation + a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */ +#define I(x, y, z) ((y) ^ ((x) | ~(z))) +#define SET(a, b, c, d, k, s, Ti)\ + t = a + I(b,c,d) + X[k] + Ti;\ + a = ROTATE_LEFT(t, s) + b + /* Do the following 16 operations. */ + SET(a, b, c, d, 0, 6, T49); + SET(d, a, b, c, 7, 10, T50); + SET(c, d, a, b, 14, 15, T51); + SET(b, c, d, a, 5, 21, T52); + SET(a, b, c, d, 12, 6, T53); + SET(d, a, b, c, 3, 10, T54); + SET(c, d, a, b, 10, 15, T55); + SET(b, c, d, a, 1, 21, T56); + SET(a, b, c, d, 8, 6, T57); + SET(d, a, b, c, 15, 10, T58); + SET(c, d, a, b, 6, 15, T59); + SET(b, c, d, a, 13, 21, T60); + SET(a, b, c, d, 4, 6, T61); + SET(d, a, b, c, 11, 10, T62); + SET(c, d, a, b, 2, 15, T63); + SET(b, c, d, a, 9, 21, T64); +#undef SET + + /* Then perform the following additions. (That is increment each + of the four registers by the value it had before this block + was started.) */ + pms->abcd[0] += a; + pms->abcd[1] += b; + pms->abcd[2] += c; + pms->abcd[3] += d; +} + +void +md5_init(md5_state_t *pms) +{ + pms->count[0] = pms->count[1] = 0; + pms->abcd[0] = 0x67452301; + pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476; + pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301; + pms->abcd[3] = 0x10325476; +} + +void +md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes) +{ + const md5_byte_t *p = data; + int left = nbytes; + int offset = (pms->count[0] >> 3) & 63; + md5_word_t nbits = (md5_word_t)(nbytes << 3); + + if (nbytes <= 0) + return; + + /* Update the message length. */ + pms->count[1] += nbytes >> 29; + pms->count[0] += nbits; + if (pms->count[0] < nbits) + pms->count[1]++; + + /* Process an initial partial block. */ + if (offset) { + int copy = (offset + nbytes > 64 ? 64 - offset : nbytes); + + memcpy(pms->buf + offset, p, copy); + if (offset + copy < 64) + return; + p += copy; + left -= copy; + md5_process(pms, pms->buf); + } + + /* Process full blocks. */ + for (; left >= 64; p += 64, left -= 64) + md5_process(pms, p); + + /* Process a final partial block. */ + if (left) + memcpy(pms->buf, p, left); +} + +void +md5_finish(md5_state_t *pms, md5_byte_t digest[16]) +{ + static const md5_byte_t pad[64] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + md5_byte_t data[8]; + int i; + + /* Save the length before padding. */ + for (i = 0; i < 8; ++i) + data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3)); + /* Pad to 56 bytes mod 64. */ + md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1); + /* Append the length. */ + md5_append(pms, data, 8); + for (i = 0; i < 16; ++i) + digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3)); +} diff --git a/src/Modules/md5.h b/src/Modules/md5.h new file mode 100644 --- /dev/null +++ b/src/Modules/md5.h @@ -0,0 +1,91 @@ +/* + Copyright (C) 1999, 2002 Aladdin Enterprises. All rights reserved. + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + L. Peter Deutsch + ghost at aladdin.com + + */ +/* $Id: md5.h 43594 2006-04-03 16:27:50Z matthias.klose $ */ +/* + Independent implementation of MD5 (RFC 1321). + + This code implements the MD5 Algorithm defined in RFC 1321, whose + text is available at + http://www.ietf.org/rfc/rfc1321.txt + The code is derived from the text of the RFC, including the test suite + (section A.5) but excluding the rest of Appendix A. It does not include + any code or documentation that is identified in the RFC as being + copyrighted. + + The original and principal author of md5.h is L. Peter Deutsch + . Other authors are noted in the change history + that follows (in reverse chronological order): + + 2002-04-13 lpd Removed support for non-ANSI compilers; removed + references to Ghostscript; clarified derivation from RFC 1321; + now handles byte order either statically or dynamically. + 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. + 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5); + added conditionalization for C++ compilation from Martin + Purschke . + 1999-05-03 lpd Original version. + */ + +#ifndef md5_INCLUDED +# define md5_INCLUDED + +/* + * This package supports both compile-time and run-time determination of CPU + * byte order. If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be + * compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is + * defined as non-zero, the code will be compiled to run only on big-endian + * CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to + * run on either big- or little-endian CPUs, but will run slightly less + * efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined. + */ + +typedef unsigned char md5_byte_t; /* 8-bit byte */ +typedef unsigned int md5_word_t; /* 32-bit word */ + +/* Define the state of the MD5 Algorithm. */ +typedef struct md5_state_s { + md5_word_t count[2]; /* message length in bits, lsw first */ + md5_word_t abcd[4]; /* digest buffer */ + md5_byte_t buf[64]; /* accumulate block */ +} md5_state_t; + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* Initialize the algorithm. */ +void md5_init(md5_state_t *pms); + +/* Append a string to the message. */ +void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes); + +/* Finish the message and return the digest. */ +void md5_finish(md5_state_t *pms, md5_byte_t digest[16]); + +#ifdef __cplusplus +} /* end extern "C" */ +#endif + +#endif /* md5_INCLUDED */ diff --git a/src/Modules/md5module.c b/src/Modules/md5module.c new file mode 100644 --- /dev/null +++ b/src/Modules/md5module.c @@ -0,0 +1,312 @@ + +/* MD5 module */ + +/* This module provides an interface to the RSA Data Security, + Inc. MD5 Message-Digest Algorithm, described in RFC 1321. + It requires the files md5c.c and md5.h (which are slightly changed + from the versions in the RFC to avoid the "global.h" file.) */ + + +/* MD5 objects */ + +#include "Python.h" +#include "structmember.h" +#include "md5.h" + +typedef struct { + PyObject_HEAD + md5_state_t md5; /* the context holder */ +} md5object; + +static PyTypeObject MD5type; + +#define is_md5object(v) ((v)->ob_type == &MD5type) + +static md5object * +newmd5object(void) +{ + md5object *md5p; + + md5p = PyObject_New(md5object, &MD5type); + if (md5p == NULL) + return NULL; + + md5_init(&md5p->md5); /* actual initialisation */ + return md5p; +} + + +/* MD5 methods */ + +static void +md5_dealloc(md5object *md5p) +{ + PyObject_Del(md5p); +} + + +/* MD5 methods-as-attributes */ + +static PyObject * +md5_update(md5object *self, PyObject *args) +{ + unsigned char *cp; + int len; + + if (!PyArg_ParseTuple(args, "s#:update", &cp, &len)) + return NULL; + + md5_append(&self->md5, cp, len); + + Py_INCREF(Py_None); + return Py_None; +} + +PyDoc_STRVAR(update_doc, +"update (arg)\n\ +\n\ +Update the md5 object with the string arg. Repeated calls are\n\ +equivalent to a single call with the concatenation of all the\n\ +arguments."); + + +static PyObject * +md5_digest(md5object *self) +{ + md5_state_t mdContext; + unsigned char aDigest[16]; + + /* make a temporary copy, and perform the final */ + mdContext = self->md5; + md5_finish(&mdContext, aDigest); + + return PyString_FromStringAndSize((char *)aDigest, 16); +} + +PyDoc_STRVAR(digest_doc, +"digest() -> string\n\ +\n\ +Return the digest of the strings passed to the update() method so\n\ +far. This is a 16-byte string which may contain non-ASCII characters,\n\ +including null bytes."); + + +static PyObject * +md5_hexdigest(md5object *self) +{ + md5_state_t mdContext; + unsigned char digest[16]; + unsigned char hexdigest[32]; + int i, j; + + /* make a temporary copy, and perform the final */ + mdContext = self->md5; + md5_finish(&mdContext, digest); + + /* Make hex version of the digest */ + for(i=j=0; i<16; i++) { + char c; + c = (digest[i] >> 4) & 0xf; + c = (c>9) ? c+'a'-10 : c + '0'; + hexdigest[j++] = c; + c = (digest[i] & 0xf); + c = (c>9) ? c+'a'-10 : c + '0'; + hexdigest[j++] = c; + } + return PyString_FromStringAndSize((char*)hexdigest, 32); +} + + +PyDoc_STRVAR(hexdigest_doc, +"hexdigest() -> string\n\ +\n\ +Like digest(), but returns the digest as a string of hexadecimal digits."); + + +static PyObject * +md5_copy(md5object *self) +{ + md5object *md5p; + + if ((md5p = newmd5object()) == NULL) + return NULL; + + md5p->md5 = self->md5; + + return (PyObject *)md5p; +} + +PyDoc_STRVAR(copy_doc, +"copy() -> md5 object\n\ +\n\ +Return a copy (``clone'') of the md5 object."); + + +static PyMethodDef md5_methods[] = { + {"update", (PyCFunction)md5_update, METH_VARARGS, update_doc}, + {"digest", (PyCFunction)md5_digest, METH_NOARGS, digest_doc}, + {"hexdigest", (PyCFunction)md5_hexdigest, METH_NOARGS, hexdigest_doc}, + {"copy", (PyCFunction)md5_copy, METH_NOARGS, copy_doc}, + {NULL, NULL} /* sentinel */ +}; + +static PyObject * +md5_get_block_size(PyObject *self, void *closure) +{ + return PyInt_FromLong(64); +} + +static PyObject * +md5_get_digest_size(PyObject *self, void *closure) +{ + return PyInt_FromLong(16); +} + +static PyObject * +md5_get_name(PyObject *self, void *closure) +{ + return PyString_FromStringAndSize("MD5", 3); +} + +static PyGetSetDef md5_getseters[] = { + {"digest_size", + (getter)md5_get_digest_size, NULL, + NULL, + NULL}, + {"block_size", + (getter)md5_get_block_size, NULL, + NULL, + NULL}, + {"name", + (getter)md5_get_name, NULL, + NULL, + NULL}, + /* the old md5 and sha modules support 'digest_size' as in PEP 247. + * the old sha module also supported 'digestsize'. ugh. */ + {"digestsize", + (getter)md5_get_digest_size, NULL, + NULL, + NULL}, + {NULL} /* Sentinel */ +}; + + +PyDoc_STRVAR(module_doc, +"This module implements the interface to RSA's MD5 message digest\n\ +algorithm (see also Internet RFC 1321). Its use is quite\n\ +straightforward: use the new() to create an md5 object. You can now\n\ +feed this object with arbitrary strings using the update() method, and\n\ +at any point you can ask it for the digest (a strong kind of 128-bit\n\ +checksum, a.k.a. ``fingerprint'') of the concatenation of the strings\n\ +fed to it so far using the digest() method.\n\ +\n\ +Functions:\n\ +\n\ +new([arg]) -- return a new md5 object, initialized with arg if provided\n\ +md5([arg]) -- DEPRECATED, same as new, but for compatibility\n\ +\n\ +Special Objects:\n\ +\n\ +MD5Type -- type object for md5 objects"); + +PyDoc_STRVAR(md5type_doc, +"An md5 represents the object used to calculate the MD5 checksum of a\n\ +string of information.\n\ +\n\ +Methods:\n\ +\n\ +update() -- updates the current digest with an additional string\n\ +digest() -- return the current digest value\n\ +hexdigest() -- return the current digest as a string of hexadecimal digits\n\ +copy() -- return a copy of the current md5 object"); + +static PyTypeObject MD5type = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "_md5.md5", /*tp_name*/ + sizeof(md5object), /*tp_size*/ + 0, /*tp_itemsize*/ + /* methods */ + (destructor)md5_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + md5type_doc, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + md5_methods, /*tp_methods*/ + 0, /*tp_members*/ + md5_getseters, /*tp_getset*/ +}; + + +/* MD5 functions */ + +static PyObject * +MD5_new(PyObject *self, PyObject *args) +{ + md5object *md5p; + unsigned char *cp = NULL; + int len = 0; + + if (!PyArg_ParseTuple(args, "|s#:new", &cp, &len)) + return NULL; + + if ((md5p = newmd5object()) == NULL) + return NULL; + + if (cp) + md5_append(&md5p->md5, cp, len); + + return (PyObject *)md5p; +} + +PyDoc_STRVAR(new_doc, +"new([arg]) -> md5 object\n\ +\n\ +Return a new md5 object. If arg is present, the method call update(arg)\n\ +is made."); + + +/* List of functions exported by this module */ + +static PyMethodDef md5_functions[] = { + {"new", (PyCFunction)MD5_new, METH_VARARGS, new_doc}, + {NULL, NULL} /* Sentinel */ +}; + + +/* Initialize this module. */ + +PyMODINIT_FUNC +init_md5(void) +{ + PyObject *m, *d; + + MD5type.ob_type = &PyType_Type; + if (PyType_Ready(&MD5type) < 0) + return; + m = Py_InitModule3("_md5", md5_functions, module_doc); + if (m == NULL) + return; + d = PyModule_GetDict(m); + PyDict_SetItemString(d, "MD5Type", (PyObject *)&MD5type); + PyModule_AddIntConstant(m, "digest_size", 16); + /* No need to check the error here, the caller will do that */ +} diff --git a/src/Modules/sha256module.c b/src/Modules/sha256module.c new file mode 100644 --- /dev/null +++ b/src/Modules/sha256module.c @@ -0,0 +1,701 @@ +/* SHA256 module */ + +/* This module provides an interface to NIST's SHA-256 and SHA-224 Algorithms */ + +/* See below for information about the original code this module was + based upon. Additional work performed by: + + Andrew Kuchling (amk at amk.ca) + Greg Stein (gstein at lyra.org) + Trevor Perrin (trevp at trevp.net) + + Copyright (C) 2005 Gregory P. Smith (greg at krypto.org) + Licensed to PSF under a Contributor Agreement. + +*/ + +/* SHA objects */ + +#include "Python.h" +#include "structmember.h" + + +/* Endianness testing and definitions */ +#define TestEndianness(variable) {int i=1; variable=PCT_BIG_ENDIAN;\ + if (*((char*)&i)==1) variable=PCT_LITTLE_ENDIAN;} + +#define PCT_LITTLE_ENDIAN 1 +#define PCT_BIG_ENDIAN 0 + +/* Some useful types */ + +typedef unsigned char SHA_BYTE; + +#if SIZEOF_INT == 4 +typedef unsigned int SHA_INT32; /* 32-bit integer */ +#else +/* not defined. compilation will die. */ +#endif + +/* The SHA block size and message digest sizes, in bytes */ + +#define SHA_BLOCKSIZE 64 +#define SHA_DIGESTSIZE 32 + +/* The structure for storing SHA info */ + +typedef struct { + PyObject_HEAD + SHA_INT32 digest[8]; /* Message digest */ + SHA_INT32 count_lo, count_hi; /* 64-bit bit count */ + SHA_BYTE data[SHA_BLOCKSIZE]; /* SHA data buffer */ + int Endianness; + int local; /* unprocessed amount in data */ + int digestsize; +} SHAobject; + +/* When run on a little-endian CPU we need to perform byte reversal on an + array of longwords. */ + +static void longReverse(SHA_INT32 *buffer, int byteCount, int Endianness) +{ + SHA_INT32 value; + + if ( Endianness == PCT_BIG_ENDIAN ) + return; + + byteCount /= sizeof(*buffer); + while (byteCount--) { + value = *buffer; + value = ( ( value & 0xFF00FF00L ) >> 8 ) | \ + ( ( value & 0x00FF00FFL ) << 8 ); + *buffer++ = ( value << 16 ) | ( value >> 16 ); + } +} + +static void SHAcopy(SHAobject *src, SHAobject *dest) +{ + dest->Endianness = src->Endianness; + dest->local = src->local; + dest->digestsize = src->digestsize; + dest->count_lo = src->count_lo; + dest->count_hi = src->count_hi; + memcpy(dest->digest, src->digest, sizeof(src->digest)); + memcpy(dest->data, src->data, sizeof(src->data)); +} + + +/* ------------------------------------------------------------------------ + * + * This code for the SHA-256 algorithm was noted as public domain. The + * original headers are pasted below. + * + * Several changes have been made to make it more compatible with the + * Python environment and desired interface. + * + */ + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * gurantee it works. + * + * Tom St Denis, tomstdenis at iahu.ca, http://libtomcrypt.org + */ + + +/* SHA256 by Tom St Denis */ + +/* Various logical functions */ +#define ROR(x, y)\ +( ((((unsigned long)(x)&0xFFFFFFFFUL)>>(unsigned long)((y)&31)) | \ +((unsigned long)(x)<<(unsigned long)(32-((y)&31)))) & 0xFFFFFFFFUL) +#define Ch(x,y,z) (z ^ (x & (y ^ z))) +#define Maj(x,y,z) (((x | y) & z) | (x & y)) +#define S(x, n) ROR((x),(n)) +#define R(x, n) (((x)&0xFFFFFFFFUL)>>(n)) +#define Sigma0(x) (S(x, 2) ^ S(x, 13) ^ S(x, 22)) +#define Sigma1(x) (S(x, 6) ^ S(x, 11) ^ S(x, 25)) +#define Gamma0(x) (S(x, 7) ^ S(x, 18) ^ R(x, 3)) +#define Gamma1(x) (S(x, 17) ^ S(x, 19) ^ R(x, 10)) + + +static void +sha_transform(SHAobject *sha_info) +{ + int i; + SHA_INT32 S[8], W[64], t0, t1; + + memcpy(W, sha_info->data, sizeof(sha_info->data)); + longReverse(W, (int)sizeof(sha_info->data), sha_info->Endianness); + + for (i = 16; i < 64; ++i) { + W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) + W[i - 16]; + } + for (i = 0; i < 8; ++i) { + S[i] = sha_info->digest[i]; + } + + /* Compress */ +#define RND(a,b,c,d,e,f,g,h,i,ki) \ + t0 = h + Sigma1(e) + Ch(e, f, g) + ki + W[i]; \ + t1 = Sigma0(a) + Maj(a, b, c); \ + d += t0; \ + h = t0 + t1; + + RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],0,0x428a2f98); + RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],1,0x71374491); + RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],2,0xb5c0fbcf); + RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],3,0xe9b5dba5); + RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],4,0x3956c25b); + RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],5,0x59f111f1); + RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],6,0x923f82a4); + RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],7,0xab1c5ed5); + RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],8,0xd807aa98); + RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],9,0x12835b01); + RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],10,0x243185be); + RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],11,0x550c7dc3); + RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],12,0x72be5d74); + RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],13,0x80deb1fe); + RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],14,0x9bdc06a7); + RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],15,0xc19bf174); + RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],16,0xe49b69c1); + RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],17,0xefbe4786); + RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],18,0x0fc19dc6); + RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],19,0x240ca1cc); + RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],20,0x2de92c6f); + RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],21,0x4a7484aa); + RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],22,0x5cb0a9dc); + RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],23,0x76f988da); + RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],24,0x983e5152); + RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],25,0xa831c66d); + RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],26,0xb00327c8); + RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],27,0xbf597fc7); + RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],28,0xc6e00bf3); + RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],29,0xd5a79147); + RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],30,0x06ca6351); + RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],31,0x14292967); + RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],32,0x27b70a85); + RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],33,0x2e1b2138); + RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],34,0x4d2c6dfc); + RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],35,0x53380d13); + RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],36,0x650a7354); + RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],37,0x766a0abb); + RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],38,0x81c2c92e); + RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],39,0x92722c85); + RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],40,0xa2bfe8a1); + RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],41,0xa81a664b); + RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],42,0xc24b8b70); + RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],43,0xc76c51a3); + RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],44,0xd192e819); + RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],45,0xd6990624); + RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],46,0xf40e3585); + RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],47,0x106aa070); + RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],48,0x19a4c116); + RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],49,0x1e376c08); + RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],50,0x2748774c); + RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],51,0x34b0bcb5); + RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],52,0x391c0cb3); + RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],53,0x4ed8aa4a); + RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],54,0x5b9cca4f); + RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],55,0x682e6ff3); + RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],56,0x748f82ee); + RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],57,0x78a5636f); + RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],58,0x84c87814); + RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],59,0x8cc70208); + RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],60,0x90befffa); + RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],61,0xa4506ceb); + RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],62,0xbef9a3f7); + RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],63,0xc67178f2); + +#undef RND + + /* feedback */ + for (i = 0; i < 8; i++) { + sha_info->digest[i] = sha_info->digest[i] + S[i]; + } + +} + + + +/* initialize the SHA digest */ + +static void +sha_init(SHAobject *sha_info) +{ + TestEndianness(sha_info->Endianness) + sha_info->digest[0] = 0x6A09E667L; + sha_info->digest[1] = 0xBB67AE85L; + sha_info->digest[2] = 0x3C6EF372L; + sha_info->digest[3] = 0xA54FF53AL; + sha_info->digest[4] = 0x510E527FL; + sha_info->digest[5] = 0x9B05688CL; + sha_info->digest[6] = 0x1F83D9ABL; + sha_info->digest[7] = 0x5BE0CD19L; + sha_info->count_lo = 0L; + sha_info->count_hi = 0L; + sha_info->local = 0; + sha_info->digestsize = 32; +} + +static void +sha224_init(SHAobject *sha_info) +{ + TestEndianness(sha_info->Endianness) + sha_info->digest[0] = 0xc1059ed8L; + sha_info->digest[1] = 0x367cd507L; + sha_info->digest[2] = 0x3070dd17L; + sha_info->digest[3] = 0xf70e5939L; + sha_info->digest[4] = 0xffc00b31L; + sha_info->digest[5] = 0x68581511L; + sha_info->digest[6] = 0x64f98fa7L; + sha_info->digest[7] = 0xbefa4fa4L; + sha_info->count_lo = 0L; + sha_info->count_hi = 0L; + sha_info->local = 0; + sha_info->digestsize = 28; +} + + +/* update the SHA digest */ + +static void +sha_update(SHAobject *sha_info, SHA_BYTE *buffer, int count) +{ + int i; + SHA_INT32 clo; + + clo = sha_info->count_lo + ((SHA_INT32) count << 3); + if (clo < sha_info->count_lo) { + ++sha_info->count_hi; + } + sha_info->count_lo = clo; + sha_info->count_hi += (SHA_INT32) count >> 29; + if (sha_info->local) { + i = SHA_BLOCKSIZE - sha_info->local; + if (i > count) { + i = count; + } + memcpy(((SHA_BYTE *) sha_info->data) + sha_info->local, buffer, i); + count -= i; + buffer += i; + sha_info->local += i; + if (sha_info->local == SHA_BLOCKSIZE) { + sha_transform(sha_info); + } + else { + return; + } + } + while (count >= SHA_BLOCKSIZE) { + memcpy(sha_info->data, buffer, SHA_BLOCKSIZE); + buffer += SHA_BLOCKSIZE; + count -= SHA_BLOCKSIZE; + sha_transform(sha_info); + } + memcpy(sha_info->data, buffer, count); + sha_info->local = count; +} + +/* finish computing the SHA digest */ + +static void +sha_final(unsigned char digest[SHA_DIGESTSIZE], SHAobject *sha_info) +{ + int count; + SHA_INT32 lo_bit_count, hi_bit_count; + + lo_bit_count = sha_info->count_lo; + hi_bit_count = sha_info->count_hi; + count = (int) ((lo_bit_count >> 3) & 0x3f); + ((SHA_BYTE *) sha_info->data)[count++] = 0x80; + if (count > SHA_BLOCKSIZE - 8) { + memset(((SHA_BYTE *) sha_info->data) + count, 0, + SHA_BLOCKSIZE - count); + sha_transform(sha_info); + memset((SHA_BYTE *) sha_info->data, 0, SHA_BLOCKSIZE - 8); + } + else { + memset(((SHA_BYTE *) sha_info->data) + count, 0, + SHA_BLOCKSIZE - 8 - count); + } + + /* GJS: note that we add the hi/lo in big-endian. sha_transform will + swap these values into host-order. */ + sha_info->data[56] = (hi_bit_count >> 24) & 0xff; + sha_info->data[57] = (hi_bit_count >> 16) & 0xff; + sha_info->data[58] = (hi_bit_count >> 8) & 0xff; + sha_info->data[59] = (hi_bit_count >> 0) & 0xff; + sha_info->data[60] = (lo_bit_count >> 24) & 0xff; + sha_info->data[61] = (lo_bit_count >> 16) & 0xff; + sha_info->data[62] = (lo_bit_count >> 8) & 0xff; + sha_info->data[63] = (lo_bit_count >> 0) & 0xff; + sha_transform(sha_info); + digest[ 0] = (unsigned char) ((sha_info->digest[0] >> 24) & 0xff); + digest[ 1] = (unsigned char) ((sha_info->digest[0] >> 16) & 0xff); + digest[ 2] = (unsigned char) ((sha_info->digest[0] >> 8) & 0xff); + digest[ 3] = (unsigned char) ((sha_info->digest[0] ) & 0xff); + digest[ 4] = (unsigned char) ((sha_info->digest[1] >> 24) & 0xff); + digest[ 5] = (unsigned char) ((sha_info->digest[1] >> 16) & 0xff); + digest[ 6] = (unsigned char) ((sha_info->digest[1] >> 8) & 0xff); + digest[ 7] = (unsigned char) ((sha_info->digest[1] ) & 0xff); + digest[ 8] = (unsigned char) ((sha_info->digest[2] >> 24) & 0xff); + digest[ 9] = (unsigned char) ((sha_info->digest[2] >> 16) & 0xff); + digest[10] = (unsigned char) ((sha_info->digest[2] >> 8) & 0xff); + digest[11] = (unsigned char) ((sha_info->digest[2] ) & 0xff); + digest[12] = (unsigned char) ((sha_info->digest[3] >> 24) & 0xff); + digest[13] = (unsigned char) ((sha_info->digest[3] >> 16) & 0xff); + digest[14] = (unsigned char) ((sha_info->digest[3] >> 8) & 0xff); + digest[15] = (unsigned char) ((sha_info->digest[3] ) & 0xff); + digest[16] = (unsigned char) ((sha_info->digest[4] >> 24) & 0xff); + digest[17] = (unsigned char) ((sha_info->digest[4] >> 16) & 0xff); + digest[18] = (unsigned char) ((sha_info->digest[4] >> 8) & 0xff); + digest[19] = (unsigned char) ((sha_info->digest[4] ) & 0xff); + digest[20] = (unsigned char) ((sha_info->digest[5] >> 24) & 0xff); + digest[21] = (unsigned char) ((sha_info->digest[5] >> 16) & 0xff); + digest[22] = (unsigned char) ((sha_info->digest[5] >> 8) & 0xff); + digest[23] = (unsigned char) ((sha_info->digest[5] ) & 0xff); + digest[24] = (unsigned char) ((sha_info->digest[6] >> 24) & 0xff); + digest[25] = (unsigned char) ((sha_info->digest[6] >> 16) & 0xff); + digest[26] = (unsigned char) ((sha_info->digest[6] >> 8) & 0xff); + digest[27] = (unsigned char) ((sha_info->digest[6] ) & 0xff); + digest[28] = (unsigned char) ((sha_info->digest[7] >> 24) & 0xff); + digest[29] = (unsigned char) ((sha_info->digest[7] >> 16) & 0xff); + digest[30] = (unsigned char) ((sha_info->digest[7] >> 8) & 0xff); + digest[31] = (unsigned char) ((sha_info->digest[7] ) & 0xff); +} + +/* + * End of copied SHA code. + * + * ------------------------------------------------------------------------ + */ + +static PyTypeObject SHA224type; +static PyTypeObject SHA256type; + + +static SHAobject * +newSHA224object(void) +{ + return (SHAobject *)PyObject_New(SHAobject, &SHA224type); +} + +static SHAobject * +newSHA256object(void) +{ + return (SHAobject *)PyObject_New(SHAobject, &SHA256type); +} + +/* Internal methods for a hash object */ + +static void +SHA_dealloc(PyObject *ptr) +{ + PyObject_Del(ptr); +} + + +/* External methods for a hash object */ + +PyDoc_STRVAR(SHA256_copy__doc__, "Return a copy of the hash object."); + +static PyObject * +SHA256_copy(SHAobject *self, PyObject *unused) +{ + SHAobject *newobj; + + if (((PyObject*)self)->ob_type == &SHA256type) { + if ( (newobj = newSHA256object())==NULL) + return NULL; + } else { + if ( (newobj = newSHA224object())==NULL) + return NULL; + } + + SHAcopy(self, newobj); + return (PyObject *)newobj; +} + +PyDoc_STRVAR(SHA256_digest__doc__, +"Return the digest value as a string of binary data."); + +static PyObject * +SHA256_digest(SHAobject *self, PyObject *unused) +{ + unsigned char digest[SHA_DIGESTSIZE]; + SHAobject temp; + + SHAcopy(self, &temp); + sha_final(digest, &temp); + return PyString_FromStringAndSize((const char *)digest, self->digestsize); +} + +PyDoc_STRVAR(SHA256_hexdigest__doc__, +"Return the digest value as a string of hexadecimal digits."); + +static PyObject * +SHA256_hexdigest(SHAobject *self, PyObject *unused) +{ + unsigned char digest[SHA_DIGESTSIZE]; + SHAobject temp; + PyObject *retval; + char *hex_digest; + int i, j; + + /* Get the raw (binary) digest value */ + SHAcopy(self, &temp); + sha_final(digest, &temp); + + /* Create a new string */ + retval = PyString_FromStringAndSize(NULL, self->digestsize * 2); + if (!retval) + return NULL; + hex_digest = PyString_AsString(retval); + if (!hex_digest) { + Py_DECREF(retval); + return NULL; + } + + /* Make hex version of the digest */ + for(i=j=0; idigestsize; i++) { + char c; + c = (digest[i] >> 4) & 0xf; + c = (c>9) ? c+'a'-10 : c + '0'; + hex_digest[j++] = c; + c = (digest[i] & 0xf); + c = (c>9) ? c+'a'-10 : c + '0'; + hex_digest[j++] = c; + } + return retval; +} + +PyDoc_STRVAR(SHA256_update__doc__, +"Update this hash object's state with the provided string."); + +static PyObject * +SHA256_update(SHAobject *self, PyObject *args) +{ + unsigned char *cp; + int len; + + if (!PyArg_ParseTuple(args, "s#:update", &cp, &len)) + return NULL; + + sha_update(self, cp, len); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyMethodDef SHA_methods[] = { + {"copy", (PyCFunction)SHA256_copy, METH_NOARGS, SHA256_copy__doc__}, + {"digest", (PyCFunction)SHA256_digest, METH_NOARGS, SHA256_digest__doc__}, + {"hexdigest", (PyCFunction)SHA256_hexdigest, METH_NOARGS, SHA256_hexdigest__doc__}, + {"update", (PyCFunction)SHA256_update, METH_VARARGS, SHA256_update__doc__}, + {NULL, NULL} /* sentinel */ +}; + +static PyObject * +SHA256_get_block_size(PyObject *self, void *closure) +{ + return PyInt_FromLong(SHA_BLOCKSIZE); +} + +static PyObject * +SHA256_get_name(PyObject *self, void *closure) +{ + if (((SHAobject *)self)->digestsize == 32) + return PyString_FromStringAndSize("SHA256", 6); + else + return PyString_FromStringAndSize("SHA224", 6); +} + +static PyGetSetDef SHA_getseters[] = { + {"block_size", + (getter)SHA256_get_block_size, NULL, + NULL, + NULL}, + {"name", + (getter)SHA256_get_name, NULL, + NULL, + NULL}, + {NULL} /* Sentinel */ +}; + +static PyMemberDef SHA_members[] = { + {"digest_size", T_INT, offsetof(SHAobject, digestsize), READONLY, NULL}, + /* the old md5 and sha modules support 'digest_size' as in PEP 247. + * the old sha module also supported 'digestsize'. ugh. */ + {"digestsize", T_INT, offsetof(SHAobject, digestsize), READONLY, NULL}, + {NULL} /* Sentinel */ +}; + +static PyTypeObject SHA224type = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "_sha256.sha224", /*tp_name*/ + sizeof(SHAobject), /*tp_size*/ + 0, /*tp_itemsize*/ + /* methods */ + SHA_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + 0, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + SHA_methods, /* tp_methods */ + SHA_members, /* tp_members */ + SHA_getseters, /* tp_getset */ +}; + +static PyTypeObject SHA256type = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "_sha256.sha256", /*tp_name*/ + sizeof(SHAobject), /*tp_size*/ + 0, /*tp_itemsize*/ + /* methods */ + SHA_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + 0, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + SHA_methods, /* tp_methods */ + SHA_members, /* tp_members */ + SHA_getseters, /* tp_getset */ +}; + + +/* The single module-level function: new() */ + +PyDoc_STRVAR(SHA256_new__doc__, +"Return a new SHA-256 hash object; optionally initialized with a string."); + +static PyObject * +SHA256_new(PyObject *self, PyObject *args, PyObject *kwdict) +{ + static char *kwlist[] = {"string", NULL}; + SHAobject *new; + unsigned char *cp = NULL; + int len; + + if (!PyArg_ParseTupleAndKeywords(args, kwdict, "|s#:new", kwlist, + &cp, &len)) { + return NULL; + } + + if ((new = newSHA256object()) == NULL) + return NULL; + + sha_init(new); + + if (PyErr_Occurred()) { + Py_DECREF(new); + return NULL; + } + if (cp) + sha_update(new, cp, len); + + return (PyObject *)new; +} + +PyDoc_STRVAR(SHA224_new__doc__, +"Return a new SHA-224 hash object; optionally initialized with a string."); + +static PyObject * +SHA224_new(PyObject *self, PyObject *args, PyObject *kwdict) +{ + static char *kwlist[] = {"string", NULL}; + SHAobject *new; + unsigned char *cp = NULL; + int len; + + if (!PyArg_ParseTupleAndKeywords(args, kwdict, "|s#:new", kwlist, + &cp, &len)) { + return NULL; + } + + if ((new = newSHA224object()) == NULL) + return NULL; + + sha224_init(new); + + if (PyErr_Occurred()) { + Py_DECREF(new); + return NULL; + } + if (cp) + sha_update(new, cp, len); + + return (PyObject *)new; +} + + +/* List of functions exported by this module */ + +static struct PyMethodDef SHA_functions[] = { + {"sha256", (PyCFunction)SHA256_new, METH_VARARGS|METH_KEYWORDS, SHA256_new__doc__}, + {"sha224", (PyCFunction)SHA224_new, METH_VARARGS|METH_KEYWORDS, SHA224_new__doc__}, + {NULL, NULL} /* Sentinel */ +}; + + +/* Initialize this module. */ + +#define insint(n,v) { PyModule_AddIntConstant(m,n,v); } + +PyMODINIT_FUNC +init_sha256(void) +{ + PyObject *m; + + SHA224type.ob_type = &PyType_Type; + if (PyType_Ready(&SHA224type) < 0) + return; + SHA256type.ob_type = &PyType_Type; + if (PyType_Ready(&SHA256type) < 0) + return; + m = Py_InitModule("_sha256", SHA_functions); + if (m == NULL) + return; +} diff --git a/src/Modules/sha512module.c b/src/Modules/sha512module.c new file mode 100644 --- /dev/null +++ b/src/Modules/sha512module.c @@ -0,0 +1,769 @@ +/* SHA512 module */ + +/* This module provides an interface to NIST's SHA-512 and SHA-384 Algorithms */ + +/* See below for information about the original code this module was + based upon. Additional work performed by: + + Andrew Kuchling (amk at amk.ca) + Greg Stein (gstein at lyra.org) + Trevor Perrin (trevp at trevp.net) + + Copyright (C) 2005 Gregory P. Smith (greg at krypto.org) + Licensed to PSF under a Contributor Agreement. + +*/ + +/* SHA objects */ + +#include "Python.h" +#include "structmember.h" + +#ifdef PY_LONG_LONG /* If no PY_LONG_LONG, don't compile anything! */ + +/* Endianness testing and definitions */ +#define TestEndianness(variable) {int i=1; variable=PCT_BIG_ENDIAN;\ + if (*((char*)&i)==1) variable=PCT_LITTLE_ENDIAN;} + +#define PCT_LITTLE_ENDIAN 1 +#define PCT_BIG_ENDIAN 0 + +/* Some useful types */ + +typedef unsigned char SHA_BYTE; + +#if SIZEOF_INT == 4 +typedef unsigned int SHA_INT32; /* 32-bit integer */ +typedef unsigned PY_LONG_LONG SHA_INT64; /* 64-bit integer */ +#else +/* not defined. compilation will die. */ +#endif + +/* The SHA block size and message digest sizes, in bytes */ + +#define SHA_BLOCKSIZE 128 +#define SHA_DIGESTSIZE 64 + +/* The structure for storing SHA info */ + +typedef struct { + PyObject_HEAD + SHA_INT64 digest[8]; /* Message digest */ + SHA_INT32 count_lo, count_hi; /* 64-bit bit count */ + SHA_BYTE data[SHA_BLOCKSIZE]; /* SHA data buffer */ + int Endianness; + int local; /* unprocessed amount in data */ + int digestsize; +} SHAobject; + +/* When run on a little-endian CPU we need to perform byte reversal on an + array of longwords. */ + +static void longReverse(SHA_INT64 *buffer, int byteCount, int Endianness) +{ + SHA_INT64 value; + + if ( Endianness == PCT_BIG_ENDIAN ) + return; + + byteCount /= sizeof(*buffer); + while (byteCount--) { + value = *buffer; + + ((unsigned char*)buffer)[0] = (unsigned char)(value >> 56) & 0xff; + ((unsigned char*)buffer)[1] = (unsigned char)(value >> 48) & 0xff; + ((unsigned char*)buffer)[2] = (unsigned char)(value >> 40) & 0xff; + ((unsigned char*)buffer)[3] = (unsigned char)(value >> 32) & 0xff; + ((unsigned char*)buffer)[4] = (unsigned char)(value >> 24) & 0xff; + ((unsigned char*)buffer)[5] = (unsigned char)(value >> 16) & 0xff; + ((unsigned char*)buffer)[6] = (unsigned char)(value >> 8) & 0xff; + ((unsigned char*)buffer)[7] = (unsigned char)(value ) & 0xff; + + buffer++; + } +} + +static void SHAcopy(SHAobject *src, SHAobject *dest) +{ + dest->Endianness = src->Endianness; + dest->local = src->local; + dest->digestsize = src->digestsize; + dest->count_lo = src->count_lo; + dest->count_hi = src->count_hi; + memcpy(dest->digest, src->digest, sizeof(src->digest)); + memcpy(dest->data, src->data, sizeof(src->data)); +} + + +/* ------------------------------------------------------------------------ + * + * This code for the SHA-512 algorithm was noted as public domain. The + * original headers are pasted below. + * + * Several changes have been made to make it more compatible with the + * Python environment and desired interface. + * + */ + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * gurantee it works. + * + * Tom St Denis, tomstdenis at iahu.ca, http://libtomcrypt.org + */ + + +/* SHA512 by Tom St Denis */ + +/* Various logical functions */ +#define ROR64(x, y) \ + ( ((((x) & 0xFFFFFFFFFFFFFFFFULL)>>((unsigned PY_LONG_LONG)(y) & 63)) | \ + ((x)<<((unsigned PY_LONG_LONG)(64-((y) & 63))))) & 0xFFFFFFFFFFFFFFFFULL) +#define Ch(x,y,z) (z ^ (x & (y ^ z))) +#define Maj(x,y,z) (((x | y) & z) | (x & y)) +#define S(x, n) ROR64((x),(n)) +#define R(x, n) (((x) & 0xFFFFFFFFFFFFFFFFULL) >> ((unsigned PY_LONG_LONG)n)) +#define Sigma0(x) (S(x, 28) ^ S(x, 34) ^ S(x, 39)) +#define Sigma1(x) (S(x, 14) ^ S(x, 18) ^ S(x, 41)) +#define Gamma0(x) (S(x, 1) ^ S(x, 8) ^ R(x, 7)) +#define Gamma1(x) (S(x, 19) ^ S(x, 61) ^ R(x, 6)) + + +static void +sha512_transform(SHAobject *sha_info) +{ + int i; + SHA_INT64 S[8], W[80], t0, t1; + + memcpy(W, sha_info->data, sizeof(sha_info->data)); + longReverse(W, (int)sizeof(sha_info->data), sha_info->Endianness); + + for (i = 16; i < 80; ++i) { + W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) + W[i - 16]; + } + for (i = 0; i < 8; ++i) { + S[i] = sha_info->digest[i]; + } + + /* Compress */ +#define RND(a,b,c,d,e,f,g,h,i,ki) \ + t0 = h + Sigma1(e) + Ch(e, f, g) + ki + W[i]; \ + t1 = Sigma0(a) + Maj(a, b, c); \ + d += t0; \ + h = t0 + t1; + + RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],0,0x428a2f98d728ae22ULL); + RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],1,0x7137449123ef65cdULL); + RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],2,0xb5c0fbcfec4d3b2fULL); + RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],3,0xe9b5dba58189dbbcULL); + RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],4,0x3956c25bf348b538ULL); + RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],5,0x59f111f1b605d019ULL); + RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],6,0x923f82a4af194f9bULL); + RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],7,0xab1c5ed5da6d8118ULL); + RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],8,0xd807aa98a3030242ULL); + RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],9,0x12835b0145706fbeULL); + RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],10,0x243185be4ee4b28cULL); + RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],11,0x550c7dc3d5ffb4e2ULL); + RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],12,0x72be5d74f27b896fULL); + RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],13,0x80deb1fe3b1696b1ULL); + RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],14,0x9bdc06a725c71235ULL); + RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],15,0xc19bf174cf692694ULL); + RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],16,0xe49b69c19ef14ad2ULL); + RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],17,0xefbe4786384f25e3ULL); + RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],18,0x0fc19dc68b8cd5b5ULL); + RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],19,0x240ca1cc77ac9c65ULL); + RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],20,0x2de92c6f592b0275ULL); + RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],21,0x4a7484aa6ea6e483ULL); + RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],22,0x5cb0a9dcbd41fbd4ULL); + RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],23,0x76f988da831153b5ULL); + RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],24,0x983e5152ee66dfabULL); + RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],25,0xa831c66d2db43210ULL); + RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],26,0xb00327c898fb213fULL); + RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],27,0xbf597fc7beef0ee4ULL); + RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],28,0xc6e00bf33da88fc2ULL); + RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],29,0xd5a79147930aa725ULL); + RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],30,0x06ca6351e003826fULL); + RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],31,0x142929670a0e6e70ULL); + RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],32,0x27b70a8546d22ffcULL); + RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],33,0x2e1b21385c26c926ULL); + RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],34,0x4d2c6dfc5ac42aedULL); + RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],35,0x53380d139d95b3dfULL); + RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],36,0x650a73548baf63deULL); + RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],37,0x766a0abb3c77b2a8ULL); + RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],38,0x81c2c92e47edaee6ULL); + RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],39,0x92722c851482353bULL); + RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],40,0xa2bfe8a14cf10364ULL); + RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],41,0xa81a664bbc423001ULL); + RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],42,0xc24b8b70d0f89791ULL); + RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],43,0xc76c51a30654be30ULL); + RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],44,0xd192e819d6ef5218ULL); + RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],45,0xd69906245565a910ULL); + RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],46,0xf40e35855771202aULL); + RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],47,0x106aa07032bbd1b8ULL); + RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],48,0x19a4c116b8d2d0c8ULL); + RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],49,0x1e376c085141ab53ULL); + RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],50,0x2748774cdf8eeb99ULL); + RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],51,0x34b0bcb5e19b48a8ULL); + RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],52,0x391c0cb3c5c95a63ULL); + RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],53,0x4ed8aa4ae3418acbULL); + RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],54,0x5b9cca4f7763e373ULL); + RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],55,0x682e6ff3d6b2b8a3ULL); + RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],56,0x748f82ee5defb2fcULL); + RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],57,0x78a5636f43172f60ULL); + RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],58,0x84c87814a1f0ab72ULL); + RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],59,0x8cc702081a6439ecULL); + RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],60,0x90befffa23631e28ULL); + RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],61,0xa4506cebde82bde9ULL); + RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],62,0xbef9a3f7b2c67915ULL); + RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],63,0xc67178f2e372532bULL); + RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],64,0xca273eceea26619cULL); + RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],65,0xd186b8c721c0c207ULL); + RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],66,0xeada7dd6cde0eb1eULL); + RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],67,0xf57d4f7fee6ed178ULL); + RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],68,0x06f067aa72176fbaULL); + RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],69,0x0a637dc5a2c898a6ULL); + RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],70,0x113f9804bef90daeULL); + RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],71,0x1b710b35131c471bULL); + RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],72,0x28db77f523047d84ULL); + RND(S[7],S[0],S[1],S[2],S[3],S[4],S[5],S[6],73,0x32caab7b40c72493ULL); + RND(S[6],S[7],S[0],S[1],S[2],S[3],S[4],S[5],74,0x3c9ebe0a15c9bebcULL); + RND(S[5],S[6],S[7],S[0],S[1],S[2],S[3],S[4],75,0x431d67c49c100d4cULL); + RND(S[4],S[5],S[6],S[7],S[0],S[1],S[2],S[3],76,0x4cc5d4becb3e42b6ULL); + RND(S[3],S[4],S[5],S[6],S[7],S[0],S[1],S[2],77,0x597f299cfc657e2aULL); + RND(S[2],S[3],S[4],S[5],S[6],S[7],S[0],S[1],78,0x5fcb6fab3ad6faecULL); + RND(S[1],S[2],S[3],S[4],S[5],S[6],S[7],S[0],79,0x6c44198c4a475817ULL); + +#undef RND + + /* feedback */ + for (i = 0; i < 8; i++) { + sha_info->digest[i] = sha_info->digest[i] + S[i]; + } + +} + + + +/* initialize the SHA digest */ + +static void +sha512_init(SHAobject *sha_info) +{ + TestEndianness(sha_info->Endianness) + sha_info->digest[0] = 0x6a09e667f3bcc908ULL; + sha_info->digest[1] = 0xbb67ae8584caa73bULL; + sha_info->digest[2] = 0x3c6ef372fe94f82bULL; + sha_info->digest[3] = 0xa54ff53a5f1d36f1ULL; + sha_info->digest[4] = 0x510e527fade682d1ULL; + sha_info->digest[5] = 0x9b05688c2b3e6c1fULL; + sha_info->digest[6] = 0x1f83d9abfb41bd6bULL; + sha_info->digest[7] = 0x5be0cd19137e2179ULL; + sha_info->count_lo = 0L; + sha_info->count_hi = 0L; + sha_info->local = 0; + sha_info->digestsize = 64; +} + +static void +sha384_init(SHAobject *sha_info) +{ + TestEndianness(sha_info->Endianness) + sha_info->digest[0] = 0xcbbb9d5dc1059ed8ULL; + sha_info->digest[1] = 0x629a292a367cd507ULL; + sha_info->digest[2] = 0x9159015a3070dd17ULL; + sha_info->digest[3] = 0x152fecd8f70e5939ULL; + sha_info->digest[4] = 0x67332667ffc00b31ULL; + sha_info->digest[5] = 0x8eb44a8768581511ULL; + sha_info->digest[6] = 0xdb0c2e0d64f98fa7ULL; + sha_info->digest[7] = 0x47b5481dbefa4fa4ULL; + sha_info->count_lo = 0L; + sha_info->count_hi = 0L; + sha_info->local = 0; + sha_info->digestsize = 48; +} + + +/* update the SHA digest */ + +static void +sha512_update(SHAobject *sha_info, SHA_BYTE *buffer, int count) +{ + int i; + SHA_INT32 clo; + + clo = sha_info->count_lo + ((SHA_INT32) count << 3); + if (clo < sha_info->count_lo) { + ++sha_info->count_hi; + } + sha_info->count_lo = clo; + sha_info->count_hi += (SHA_INT32) count >> 29; + if (sha_info->local) { + i = SHA_BLOCKSIZE - sha_info->local; + if (i > count) { + i = count; + } + memcpy(((SHA_BYTE *) sha_info->data) + sha_info->local, buffer, i); + count -= i; + buffer += i; + sha_info->local += i; + if (sha_info->local == SHA_BLOCKSIZE) { + sha512_transform(sha_info); + } + else { + return; + } + } + while (count >= SHA_BLOCKSIZE) { + memcpy(sha_info->data, buffer, SHA_BLOCKSIZE); + buffer += SHA_BLOCKSIZE; + count -= SHA_BLOCKSIZE; + sha512_transform(sha_info); + } + memcpy(sha_info->data, buffer, count); + sha_info->local = count; +} + +/* finish computing the SHA digest */ + +static void +sha512_final(unsigned char digest[SHA_DIGESTSIZE], SHAobject *sha_info) +{ + int count; + SHA_INT32 lo_bit_count, hi_bit_count; + + lo_bit_count = sha_info->count_lo; + hi_bit_count = sha_info->count_hi; + count = (int) ((lo_bit_count >> 3) & 0x7f); + ((SHA_BYTE *) sha_info->data)[count++] = 0x80; + if (count > SHA_BLOCKSIZE - 16) { + memset(((SHA_BYTE *) sha_info->data) + count, 0, + SHA_BLOCKSIZE - count); + sha512_transform(sha_info); + memset((SHA_BYTE *) sha_info->data, 0, SHA_BLOCKSIZE - 16); + } + else { + memset(((SHA_BYTE *) sha_info->data) + count, 0, + SHA_BLOCKSIZE - 16 - count); + } + + /* GJS: note that we add the hi/lo in big-endian. sha512_transform will + swap these values into host-order. */ + sha_info->data[112] = 0; + sha_info->data[113] = 0; + sha_info->data[114] = 0; + sha_info->data[115] = 0; + sha_info->data[116] = 0; + sha_info->data[117] = 0; + sha_info->data[118] = 0; + sha_info->data[119] = 0; + sha_info->data[120] = (hi_bit_count >> 24) & 0xff; + sha_info->data[121] = (hi_bit_count >> 16) & 0xff; + sha_info->data[122] = (hi_bit_count >> 8) & 0xff; + sha_info->data[123] = (hi_bit_count >> 0) & 0xff; + sha_info->data[124] = (lo_bit_count >> 24) & 0xff; + sha_info->data[125] = (lo_bit_count >> 16) & 0xff; + sha_info->data[126] = (lo_bit_count >> 8) & 0xff; + sha_info->data[127] = (lo_bit_count >> 0) & 0xff; + sha512_transform(sha_info); + digest[ 0] = (unsigned char) ((sha_info->digest[0] >> 56) & 0xff); + digest[ 1] = (unsigned char) ((sha_info->digest[0] >> 48) & 0xff); + digest[ 2] = (unsigned char) ((sha_info->digest[0] >> 40) & 0xff); + digest[ 3] = (unsigned char) ((sha_info->digest[0] >> 32) & 0xff); + digest[ 4] = (unsigned char) ((sha_info->digest[0] >> 24) & 0xff); + digest[ 5] = (unsigned char) ((sha_info->digest[0] >> 16) & 0xff); + digest[ 6] = (unsigned char) ((sha_info->digest[0] >> 8) & 0xff); + digest[ 7] = (unsigned char) ((sha_info->digest[0] ) & 0xff); + digest[ 8] = (unsigned char) ((sha_info->digest[1] >> 56) & 0xff); + digest[ 9] = (unsigned char) ((sha_info->digest[1] >> 48) & 0xff); + digest[10] = (unsigned char) ((sha_info->digest[1] >> 40) & 0xff); + digest[11] = (unsigned char) ((sha_info->digest[1] >> 32) & 0xff); + digest[12] = (unsigned char) ((sha_info->digest[1] >> 24) & 0xff); + digest[13] = (unsigned char) ((sha_info->digest[1] >> 16) & 0xff); + digest[14] = (unsigned char) ((sha_info->digest[1] >> 8) & 0xff); + digest[15] = (unsigned char) ((sha_info->digest[1] ) & 0xff); + digest[16] = (unsigned char) ((sha_info->digest[2] >> 56) & 0xff); + digest[17] = (unsigned char) ((sha_info->digest[2] >> 48) & 0xff); + digest[18] = (unsigned char) ((sha_info->digest[2] >> 40) & 0xff); + digest[19] = (unsigned char) ((sha_info->digest[2] >> 32) & 0xff); + digest[20] = (unsigned char) ((sha_info->digest[2] >> 24) & 0xff); + digest[21] = (unsigned char) ((sha_info->digest[2] >> 16) & 0xff); + digest[22] = (unsigned char) ((sha_info->digest[2] >> 8) & 0xff); + digest[23] = (unsigned char) ((sha_info->digest[2] ) & 0xff); + digest[24] = (unsigned char) ((sha_info->digest[3] >> 56) & 0xff); + digest[25] = (unsigned char) ((sha_info->digest[3] >> 48) & 0xff); + digest[26] = (unsigned char) ((sha_info->digest[3] >> 40) & 0xff); + digest[27] = (unsigned char) ((sha_info->digest[3] >> 32) & 0xff); + digest[28] = (unsigned char) ((sha_info->digest[3] >> 24) & 0xff); + digest[29] = (unsigned char) ((sha_info->digest[3] >> 16) & 0xff); + digest[30] = (unsigned char) ((sha_info->digest[3] >> 8) & 0xff); + digest[31] = (unsigned char) ((sha_info->digest[3] ) & 0xff); + digest[32] = (unsigned char) ((sha_info->digest[4] >> 56) & 0xff); + digest[33] = (unsigned char) ((sha_info->digest[4] >> 48) & 0xff); + digest[34] = (unsigned char) ((sha_info->digest[4] >> 40) & 0xff); + digest[35] = (unsigned char) ((sha_info->digest[4] >> 32) & 0xff); + digest[36] = (unsigned char) ((sha_info->digest[4] >> 24) & 0xff); + digest[37] = (unsigned char) ((sha_info->digest[4] >> 16) & 0xff); + digest[38] = (unsigned char) ((sha_info->digest[4] >> 8) & 0xff); + digest[39] = (unsigned char) ((sha_info->digest[4] ) & 0xff); + digest[40] = (unsigned char) ((sha_info->digest[5] >> 56) & 0xff); + digest[41] = (unsigned char) ((sha_info->digest[5] >> 48) & 0xff); + digest[42] = (unsigned char) ((sha_info->digest[5] >> 40) & 0xff); + digest[43] = (unsigned char) ((sha_info->digest[5] >> 32) & 0xff); + digest[44] = (unsigned char) ((sha_info->digest[5] >> 24) & 0xff); + digest[45] = (unsigned char) ((sha_info->digest[5] >> 16) & 0xff); + digest[46] = (unsigned char) ((sha_info->digest[5] >> 8) & 0xff); + digest[47] = (unsigned char) ((sha_info->digest[5] ) & 0xff); + digest[48] = (unsigned char) ((sha_info->digest[6] >> 56) & 0xff); + digest[49] = (unsigned char) ((sha_info->digest[6] >> 48) & 0xff); + digest[50] = (unsigned char) ((sha_info->digest[6] >> 40) & 0xff); + digest[51] = (unsigned char) ((sha_info->digest[6] >> 32) & 0xff); + digest[52] = (unsigned char) ((sha_info->digest[6] >> 24) & 0xff); + digest[53] = (unsigned char) ((sha_info->digest[6] >> 16) & 0xff); + digest[54] = (unsigned char) ((sha_info->digest[6] >> 8) & 0xff); + digest[55] = (unsigned char) ((sha_info->digest[6] ) & 0xff); + digest[56] = (unsigned char) ((sha_info->digest[7] >> 56) & 0xff); + digest[57] = (unsigned char) ((sha_info->digest[7] >> 48) & 0xff); + digest[58] = (unsigned char) ((sha_info->digest[7] >> 40) & 0xff); + digest[59] = (unsigned char) ((sha_info->digest[7] >> 32) & 0xff); + digest[60] = (unsigned char) ((sha_info->digest[7] >> 24) & 0xff); + digest[61] = (unsigned char) ((sha_info->digest[7] >> 16) & 0xff); + digest[62] = (unsigned char) ((sha_info->digest[7] >> 8) & 0xff); + digest[63] = (unsigned char) ((sha_info->digest[7] ) & 0xff); +} + +/* + * End of copied SHA code. + * + * ------------------------------------------------------------------------ + */ + +static PyTypeObject SHA384type; +static PyTypeObject SHA512type; + + +static SHAobject * +newSHA384object(void) +{ + return (SHAobject *)PyObject_New(SHAobject, &SHA384type); +} + +static SHAobject * +newSHA512object(void) +{ + return (SHAobject *)PyObject_New(SHAobject, &SHA512type); +} + +/* Internal methods for a hash object */ + +static void +SHA512_dealloc(PyObject *ptr) +{ + PyObject_Del(ptr); +} + + +/* External methods for a hash object */ + +PyDoc_STRVAR(SHA512_copy__doc__, "Return a copy of the hash object."); + +static PyObject * +SHA512_copy(SHAobject *self, PyObject *unused) +{ + SHAobject *newobj; + + if (((PyObject*)self)->ob_type == &SHA512type) { + if ( (newobj = newSHA512object())==NULL) + return NULL; + } else { + if ( (newobj = newSHA384object())==NULL) + return NULL; + } + + SHAcopy(self, newobj); + return (PyObject *)newobj; +} + +PyDoc_STRVAR(SHA512_digest__doc__, +"Return the digest value as a string of binary data."); + +static PyObject * +SHA512_digest(SHAobject *self, PyObject *unused) +{ + unsigned char digest[SHA_DIGESTSIZE]; + SHAobject temp; + + SHAcopy(self, &temp); + sha512_final(digest, &temp); + return PyString_FromStringAndSize((const char *)digest, self->digestsize); +} + +PyDoc_STRVAR(SHA512_hexdigest__doc__, +"Return the digest value as a string of hexadecimal digits."); + +static PyObject * +SHA512_hexdigest(SHAobject *self, PyObject *unused) +{ + unsigned char digest[SHA_DIGESTSIZE]; + SHAobject temp; + PyObject *retval; + char *hex_digest; + int i, j; + + /* Get the raw (binary) digest value */ + SHAcopy(self, &temp); + sha512_final(digest, &temp); + + /* Create a new string */ + retval = PyString_FromStringAndSize(NULL, self->digestsize * 2); + if (!retval) + return NULL; + hex_digest = PyString_AsString(retval); + if (!hex_digest) { + Py_DECREF(retval); + return NULL; + } + + /* Make hex version of the digest */ + for (i=j=0; idigestsize; i++) { + char c; + c = (digest[i] >> 4) & 0xf; + c = (c>9) ? c+'a'-10 : c + '0'; + hex_digest[j++] = c; + c = (digest[i] & 0xf); + c = (c>9) ? c+'a'-10 : c + '0'; + hex_digest[j++] = c; + } + return retval; +} + +PyDoc_STRVAR(SHA512_update__doc__, +"Update this hash object's state with the provided string."); + +static PyObject * +SHA512_update(SHAobject *self, PyObject *args) +{ + unsigned char *cp; + int len; + + if (!PyArg_ParseTuple(args, "s#:update", &cp, &len)) + return NULL; + + sha512_update(self, cp, len); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyMethodDef SHA_methods[] = { + {"copy", (PyCFunction)SHA512_copy, METH_NOARGS, SHA512_copy__doc__}, + {"digest", (PyCFunction)SHA512_digest, METH_NOARGS, SHA512_digest__doc__}, + {"hexdigest", (PyCFunction)SHA512_hexdigest, METH_NOARGS, SHA512_hexdigest__doc__}, + {"update", (PyCFunction)SHA512_update, METH_VARARGS, SHA512_update__doc__}, + {NULL, NULL} /* sentinel */ +}; + +static PyObject * +SHA512_get_block_size(PyObject *self, void *closure) +{ + return PyInt_FromLong(SHA_BLOCKSIZE); +} + +static PyObject * +SHA512_get_name(PyObject *self, void *closure) +{ + if (((SHAobject *)self)->digestsize == 64) + return PyString_FromStringAndSize("SHA512", 6); + else + return PyString_FromStringAndSize("SHA384", 6); +} + +static PyGetSetDef SHA_getseters[] = { + {"block_size", + (getter)SHA512_get_block_size, NULL, + NULL, + NULL}, + {"name", + (getter)SHA512_get_name, NULL, + NULL, + NULL}, + {NULL} /* Sentinel */ +}; + +static PyMemberDef SHA_members[] = { + {"digest_size", T_INT, offsetof(SHAobject, digestsize), READONLY, NULL}, + /* the old md5 and sha modules support 'digest_size' as in PEP 247. + * the old sha module also supported 'digestsize'. ugh. */ + {"digestsize", T_INT, offsetof(SHAobject, digestsize), READONLY, NULL}, + {NULL} /* Sentinel */ +}; + +static PyTypeObject SHA384type = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "_sha512.sha384", /*tp_name*/ + sizeof(SHAobject), /*tp_size*/ + 0, /*tp_itemsize*/ + /* methods */ + SHA512_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + 0, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + SHA_methods, /* tp_methods */ + SHA_members, /* tp_members */ + SHA_getseters, /* tp_getset */ +}; + +static PyTypeObject SHA512type = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "_sha512.sha512", /*tp_name*/ + sizeof(SHAobject), /*tp_size*/ + 0, /*tp_itemsize*/ + /* methods */ + SHA512_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + 0, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + SHA_methods, /* tp_methods */ + SHA_members, /* tp_members */ + SHA_getseters, /* tp_getset */ +}; + + +/* The single module-level function: new() */ + +PyDoc_STRVAR(SHA512_new__doc__, +"Return a new SHA-512 hash object; optionally initialized with a string."); + +static PyObject * +SHA512_new(PyObject *self, PyObject *args, PyObject *kwdict) +{ + static char *kwlist[] = {"string", NULL}; + SHAobject *new; + unsigned char *cp = NULL; + int len; + + if (!PyArg_ParseTupleAndKeywords(args, kwdict, "|s#:new", kwlist, + &cp, &len)) { + return NULL; + } + + if ((new = newSHA512object()) == NULL) + return NULL; + + sha512_init(new); + + if (PyErr_Occurred()) { + Py_DECREF(new); + return NULL; + } + if (cp) + sha512_update(new, cp, len); + + return (PyObject *)new; +} + +PyDoc_STRVAR(SHA384_new__doc__, +"Return a new SHA-384 hash object; optionally initialized with a string."); + +static PyObject * +SHA384_new(PyObject *self, PyObject *args, PyObject *kwdict) +{ + static char *kwlist[] = {"string", NULL}; + SHAobject *new; + unsigned char *cp = NULL; + int len; + + if (!PyArg_ParseTupleAndKeywords(args, kwdict, "|s#:new", kwlist, + &cp, &len)) { + return NULL; + } + + if ((new = newSHA384object()) == NULL) + return NULL; + + sha384_init(new); + + if (PyErr_Occurred()) { + Py_DECREF(new); + return NULL; + } + if (cp) + sha512_update(new, cp, len); + + return (PyObject *)new; +} + + +/* List of functions exported by this module */ + +static struct PyMethodDef SHA_functions[] = { + {"sha512", (PyCFunction)SHA512_new, METH_VARARGS|METH_KEYWORDS, SHA512_new__doc__}, + {"sha384", (PyCFunction)SHA384_new, METH_VARARGS|METH_KEYWORDS, SHA384_new__doc__}, + {NULL, NULL} /* Sentinel */ +}; + + +/* Initialize this module. */ + +#define insint(n,v) { PyModule_AddIntConstant(m,n,v); } + +PyMODINIT_FUNC +init_sha512(void) +{ + PyObject *m; + + SHA384type.ob_type = &PyType_Type; + if (PyType_Ready(&SHA384type) < 0) + return; + SHA512type.ob_type = &PyType_Type; + if (PyType_Ready(&SHA512type) < 0) + return; + m = Py_InitModule("_sha512", SHA_functions); + if (m == NULL) + return; +} + +#endif diff --git a/src/Modules/shamodule.c b/src/Modules/shamodule.c new file mode 100644 --- /dev/null +++ b/src/Modules/shamodule.c @@ -0,0 +1,593 @@ +/* SHA module */ + +/* This module provides an interface to NIST's Secure Hash Algorithm */ + +/* See below for information about the original code this module was + based upon. Additional work performed by: + + Andrew Kuchling (amk at amk.ca) + Greg Stein (gstein at lyra.org) + + Copyright (C) 2005 Gregory P. Smith (greg at krypto.org) + Licensed to PSF under a Contributor Agreement. + +*/ + +/* SHA objects */ + +#include "Python.h" +#include "structmember.h" + + +/* Endianness testing and definitions */ +#define TestEndianness(variable) {int i=1; variable=PCT_BIG_ENDIAN;\ + if (*((char*)&i)==1) variable=PCT_LITTLE_ENDIAN;} + +#define PCT_LITTLE_ENDIAN 1 +#define PCT_BIG_ENDIAN 0 + +/* Some useful types */ + +typedef unsigned char SHA_BYTE; + +#if SIZEOF_INT == 4 +typedef unsigned int SHA_INT32; /* 32-bit integer */ +#else +/* not defined. compilation will die. */ +#endif + +/* The SHA block size and message digest sizes, in bytes */ + +#define SHA_BLOCKSIZE 64 +#define SHA_DIGESTSIZE 20 + +/* The structure for storing SHS info */ + +typedef struct { + PyObject_HEAD + SHA_INT32 digest[5]; /* Message digest */ + SHA_INT32 count_lo, count_hi; /* 64-bit bit count */ + SHA_BYTE data[SHA_BLOCKSIZE]; /* SHA data buffer */ + int Endianness; + int local; /* unprocessed amount in data */ +} SHAobject; + +/* When run on a little-endian CPU we need to perform byte reversal on an + array of longwords. */ + +static void longReverse(SHA_INT32 *buffer, int byteCount, int Endianness) +{ + SHA_INT32 value; + + if ( Endianness == PCT_BIG_ENDIAN ) + return; + + byteCount /= sizeof(*buffer); + while (byteCount--) { + value = *buffer; + value = ( ( value & 0xFF00FF00L ) >> 8 ) | \ + ( ( value & 0x00FF00FFL ) << 8 ); + *buffer++ = ( value << 16 ) | ( value >> 16 ); + } +} + +static void SHAcopy(SHAobject *src, SHAobject *dest) +{ + dest->Endianness = src->Endianness; + dest->local = src->local; + dest->count_lo = src->count_lo; + dest->count_hi = src->count_hi; + memcpy(dest->digest, src->digest, sizeof(src->digest)); + memcpy(dest->data, src->data, sizeof(src->data)); +} + + +/* ------------------------------------------------------------------------ + * + * This code for the SHA algorithm was noted as public domain. The original + * headers are pasted below. + * + * Several changes have been made to make it more compatible with the + * Python environment and desired interface. + * + */ + +/* NIST Secure Hash Algorithm */ +/* heavily modified by Uwe Hollerbach */ +/* from Peter C. Gutmann's implementation as found in */ +/* Applied Cryptography by Bruce Schneier */ +/* Further modifications to include the "UNRAVEL" stuff, below */ + +/* This code is in the public domain */ + +/* UNRAVEL should be fastest & biggest */ +/* UNROLL_LOOPS should be just as big, but slightly slower */ +/* both undefined should be smallest and slowest */ + +#define UNRAVEL +/* #define UNROLL_LOOPS */ + +/* The SHA f()-functions. The f1 and f3 functions can be optimized to + save one boolean operation each - thanks to Rich Schroeppel, + rcs at cs.arizona.edu for discovering this */ + +/*#define f1(x,y,z) ((x & y) | (~x & z)) // Rounds 0-19 */ +#define f1(x,y,z) (z ^ (x & (y ^ z))) /* Rounds 0-19 */ +#define f2(x,y,z) (x ^ y ^ z) /* Rounds 20-39 */ +/*#define f3(x,y,z) ((x & y) | (x & z) | (y & z)) // Rounds 40-59 */ +#define f3(x,y,z) ((x & y) | (z & (x | y))) /* Rounds 40-59 */ +#define f4(x,y,z) (x ^ y ^ z) /* Rounds 60-79 */ + +/* SHA constants */ + +#define CONST1 0x5a827999L /* Rounds 0-19 */ +#define CONST2 0x6ed9eba1L /* Rounds 20-39 */ +#define CONST3 0x8f1bbcdcL /* Rounds 40-59 */ +#define CONST4 0xca62c1d6L /* Rounds 60-79 */ + +/* 32-bit rotate */ + +#define R32(x,n) ((x << n) | (x >> (32 - n))) + +/* the generic case, for when the overall rotation is not unraveled */ + +#define FG(n) \ + T = R32(A,5) + f##n(B,C,D) + E + *WP++ + CONST##n; \ + E = D; D = C; C = R32(B,30); B = A; A = T + +/* specific cases, for when the overall rotation is unraveled */ + +#define FA(n) \ + T = R32(A,5) + f##n(B,C,D) + E + *WP++ + CONST##n; B = R32(B,30) + +#define FB(n) \ + E = R32(T,5) + f##n(A,B,C) + D + *WP++ + CONST##n; A = R32(A,30) + +#define FC(n) \ + D = R32(E,5) + f##n(T,A,B) + C + *WP++ + CONST##n; T = R32(T,30) + +#define FD(n) \ + C = R32(D,5) + f##n(E,T,A) + B + *WP++ + CONST##n; E = R32(E,30) + +#define FE(n) \ + B = R32(C,5) + f##n(D,E,T) + A + *WP++ + CONST##n; D = R32(D,30) + +#define FT(n) \ + A = R32(B,5) + f##n(C,D,E) + T + *WP++ + CONST##n; C = R32(C,30) + +/* do SHA transformation */ + +static void +sha_transform(SHAobject *sha_info) +{ + int i; + SHA_INT32 T, A, B, C, D, E, W[80], *WP; + + memcpy(W, sha_info->data, sizeof(sha_info->data)); + longReverse(W, (int)sizeof(sha_info->data), sha_info->Endianness); + + for (i = 16; i < 80; ++i) { + W[i] = W[i-3] ^ W[i-8] ^ W[i-14] ^ W[i-16]; + + /* extra rotation fix */ + W[i] = R32(W[i], 1); + } + A = sha_info->digest[0]; + B = sha_info->digest[1]; + C = sha_info->digest[2]; + D = sha_info->digest[3]; + E = sha_info->digest[4]; + WP = W; +#ifdef UNRAVEL + FA(1); FB(1); FC(1); FD(1); FE(1); FT(1); FA(1); FB(1); FC(1); FD(1); + FE(1); FT(1); FA(1); FB(1); FC(1); FD(1); FE(1); FT(1); FA(1); FB(1); + FC(2); FD(2); FE(2); FT(2); FA(2); FB(2); FC(2); FD(2); FE(2); FT(2); + FA(2); FB(2); FC(2); FD(2); FE(2); FT(2); FA(2); FB(2); FC(2); FD(2); + FE(3); FT(3); FA(3); FB(3); FC(3); FD(3); FE(3); FT(3); FA(3); FB(3); + FC(3); FD(3); FE(3); FT(3); FA(3); FB(3); FC(3); FD(3); FE(3); FT(3); + FA(4); FB(4); FC(4); FD(4); FE(4); FT(4); FA(4); FB(4); FC(4); FD(4); + FE(4); FT(4); FA(4); FB(4); FC(4); FD(4); FE(4); FT(4); FA(4); FB(4); + sha_info->digest[0] += E; + sha_info->digest[1] += T; + sha_info->digest[2] += A; + sha_info->digest[3] += B; + sha_info->digest[4] += C; +#else /* !UNRAVEL */ +#ifdef UNROLL_LOOPS + FG(1); FG(1); FG(1); FG(1); FG(1); FG(1); FG(1); FG(1); FG(1); FG(1); + FG(1); FG(1); FG(1); FG(1); FG(1); FG(1); FG(1); FG(1); FG(1); FG(1); + FG(2); FG(2); FG(2); FG(2); FG(2); FG(2); FG(2); FG(2); FG(2); FG(2); + FG(2); FG(2); FG(2); FG(2); FG(2); FG(2); FG(2); FG(2); FG(2); FG(2); + FG(3); FG(3); FG(3); FG(3); FG(3); FG(3); FG(3); FG(3); FG(3); FG(3); + FG(3); FG(3); FG(3); FG(3); FG(3); FG(3); FG(3); FG(3); FG(3); FG(3); + FG(4); FG(4); FG(4); FG(4); FG(4); FG(4); FG(4); FG(4); FG(4); FG(4); + FG(4); FG(4); FG(4); FG(4); FG(4); FG(4); FG(4); FG(4); FG(4); FG(4); +#else /* !UNROLL_LOOPS */ + for (i = 0; i < 20; ++i) { FG(1); } + for (i = 20; i < 40; ++i) { FG(2); } + for (i = 40; i < 60; ++i) { FG(3); } + for (i = 60; i < 80; ++i) { FG(4); } +#endif /* !UNROLL_LOOPS */ + sha_info->digest[0] += A; + sha_info->digest[1] += B; + sha_info->digest[2] += C; + sha_info->digest[3] += D; + sha_info->digest[4] += E; +#endif /* !UNRAVEL */ +} + +/* initialize the SHA digest */ + +static void +sha_init(SHAobject *sha_info) +{ + TestEndianness(sha_info->Endianness) + + sha_info->digest[0] = 0x67452301L; + sha_info->digest[1] = 0xefcdab89L; + sha_info->digest[2] = 0x98badcfeL; + sha_info->digest[3] = 0x10325476L; + sha_info->digest[4] = 0xc3d2e1f0L; + sha_info->count_lo = 0L; + sha_info->count_hi = 0L; + sha_info->local = 0; +} + +/* update the SHA digest */ + +static void +sha_update(SHAobject *sha_info, SHA_BYTE *buffer, int count) +{ + int i; + SHA_INT32 clo; + + clo = sha_info->count_lo + ((SHA_INT32) count << 3); + if (clo < sha_info->count_lo) { + ++sha_info->count_hi; + } + sha_info->count_lo = clo; + sha_info->count_hi += (SHA_INT32) count >> 29; + if (sha_info->local) { + i = SHA_BLOCKSIZE - sha_info->local; + if (i > count) { + i = count; + } + memcpy(((SHA_BYTE *) sha_info->data) + sha_info->local, buffer, i); + count -= i; + buffer += i; + sha_info->local += i; + if (sha_info->local == SHA_BLOCKSIZE) { + sha_transform(sha_info); + } + else { + return; + } + } + while (count >= SHA_BLOCKSIZE) { + memcpy(sha_info->data, buffer, SHA_BLOCKSIZE); + buffer += SHA_BLOCKSIZE; + count -= SHA_BLOCKSIZE; + sha_transform(sha_info); + } + memcpy(sha_info->data, buffer, count); + sha_info->local = count; +} + +/* finish computing the SHA digest */ + +static void +sha_final(unsigned char digest[20], SHAobject *sha_info) +{ + int count; + SHA_INT32 lo_bit_count, hi_bit_count; + + lo_bit_count = sha_info->count_lo; + hi_bit_count = sha_info->count_hi; + count = (int) ((lo_bit_count >> 3) & 0x3f); + ((SHA_BYTE *) sha_info->data)[count++] = 0x80; + if (count > SHA_BLOCKSIZE - 8) { + memset(((SHA_BYTE *) sha_info->data) + count, 0, + SHA_BLOCKSIZE - count); + sha_transform(sha_info); + memset((SHA_BYTE *) sha_info->data, 0, SHA_BLOCKSIZE - 8); + } + else { + memset(((SHA_BYTE *) sha_info->data) + count, 0, + SHA_BLOCKSIZE - 8 - count); + } + + /* GJS: note that we add the hi/lo in big-endian. sha_transform will + swap these values into host-order. */ + sha_info->data[56] = (hi_bit_count >> 24) & 0xff; + sha_info->data[57] = (hi_bit_count >> 16) & 0xff; + sha_info->data[58] = (hi_bit_count >> 8) & 0xff; + sha_info->data[59] = (hi_bit_count >> 0) & 0xff; + sha_info->data[60] = (lo_bit_count >> 24) & 0xff; + sha_info->data[61] = (lo_bit_count >> 16) & 0xff; + sha_info->data[62] = (lo_bit_count >> 8) & 0xff; + sha_info->data[63] = (lo_bit_count >> 0) & 0xff; + sha_transform(sha_info); + digest[ 0] = (unsigned char) ((sha_info->digest[0] >> 24) & 0xff); + digest[ 1] = (unsigned char) ((sha_info->digest[0] >> 16) & 0xff); + digest[ 2] = (unsigned char) ((sha_info->digest[0] >> 8) & 0xff); + digest[ 3] = (unsigned char) ((sha_info->digest[0] ) & 0xff); + digest[ 4] = (unsigned char) ((sha_info->digest[1] >> 24) & 0xff); + digest[ 5] = (unsigned char) ((sha_info->digest[1] >> 16) & 0xff); + digest[ 6] = (unsigned char) ((sha_info->digest[1] >> 8) & 0xff); + digest[ 7] = (unsigned char) ((sha_info->digest[1] ) & 0xff); + digest[ 8] = (unsigned char) ((sha_info->digest[2] >> 24) & 0xff); + digest[ 9] = (unsigned char) ((sha_info->digest[2] >> 16) & 0xff); + digest[10] = (unsigned char) ((sha_info->digest[2] >> 8) & 0xff); + digest[11] = (unsigned char) ((sha_info->digest[2] ) & 0xff); + digest[12] = (unsigned char) ((sha_info->digest[3] >> 24) & 0xff); + digest[13] = (unsigned char) ((sha_info->digest[3] >> 16) & 0xff); + digest[14] = (unsigned char) ((sha_info->digest[3] >> 8) & 0xff); + digest[15] = (unsigned char) ((sha_info->digest[3] ) & 0xff); + digest[16] = (unsigned char) ((sha_info->digest[4] >> 24) & 0xff); + digest[17] = (unsigned char) ((sha_info->digest[4] >> 16) & 0xff); + digest[18] = (unsigned char) ((sha_info->digest[4] >> 8) & 0xff); + digest[19] = (unsigned char) ((sha_info->digest[4] ) & 0xff); +} + +/* + * End of copied SHA code. + * + * ------------------------------------------------------------------------ + */ + +static PyTypeObject SHAtype; + + +static SHAobject * +newSHAobject(void) +{ + return (SHAobject *)PyObject_New(SHAobject, &SHAtype); +} + +/* Internal methods for a hashing object */ + +static void +SHA_dealloc(PyObject *ptr) +{ + PyObject_Del(ptr); +} + + +/* External methods for a hashing object */ + +PyDoc_STRVAR(SHA_copy__doc__, "Return a copy of the hashing object."); + +static PyObject * +SHA_copy(SHAobject *self, PyObject *unused) +{ + SHAobject *newobj; + + if ( (newobj = newSHAobject())==NULL) + return NULL; + + SHAcopy(self, newobj); + return (PyObject *)newobj; +} + +PyDoc_STRVAR(SHA_digest__doc__, +"Return the digest value as a string of binary data."); + +static PyObject * +SHA_digest(SHAobject *self, PyObject *unused) +{ + unsigned char digest[SHA_DIGESTSIZE]; + SHAobject temp; + + SHAcopy(self, &temp); + sha_final(digest, &temp); + return PyString_FromStringAndSize((const char *)digest, sizeof(digest)); +} + +PyDoc_STRVAR(SHA_hexdigest__doc__, +"Return the digest value as a string of hexadecimal digits."); + +static PyObject * +SHA_hexdigest(SHAobject *self, PyObject *unused) +{ + unsigned char digest[SHA_DIGESTSIZE]; + SHAobject temp; + PyObject *retval; + char *hex_digest; + int i, j; + + /* Get the raw (binary) digest value */ + SHAcopy(self, &temp); + sha_final(digest, &temp); + + /* Create a new string */ + retval = PyString_FromStringAndSize(NULL, sizeof(digest) * 2); + if (!retval) + return NULL; + hex_digest = PyString_AsString(retval); + if (!hex_digest) { + Py_DECREF(retval); + return NULL; + } + + /* Make hex version of the digest */ + for(i=j=0; i> 4) & 0xf; + c = (c>9) ? c+'a'-10 : c + '0'; + hex_digest[j++] = c; + c = (digest[i] & 0xf); + c = (c>9) ? c+'a'-10 : c + '0'; + hex_digest[j++] = c; + } + return retval; +} + +PyDoc_STRVAR(SHA_update__doc__, +"Update this hashing object's state with the provided string."); + +static PyObject * +SHA_update(SHAobject *self, PyObject *args) +{ + unsigned char *cp; + int len; + + if (!PyArg_ParseTuple(args, "s#:update", &cp, &len)) + return NULL; + + sha_update(self, cp, len); + + Py_INCREF(Py_None); + return Py_None; +} + +static PyMethodDef SHA_methods[] = { + {"copy", (PyCFunction)SHA_copy, METH_NOARGS, SHA_copy__doc__}, + {"digest", (PyCFunction)SHA_digest, METH_NOARGS, SHA_digest__doc__}, + {"hexdigest", (PyCFunction)SHA_hexdigest, METH_NOARGS, SHA_hexdigest__doc__}, + {"update", (PyCFunction)SHA_update, METH_VARARGS, SHA_update__doc__}, + {NULL, NULL} /* sentinel */ +}; + +static PyObject * +SHA_get_block_size(PyObject *self, void *closure) +{ + return PyInt_FromLong(SHA_BLOCKSIZE); +} + +static PyObject * +SHA_get_digest_size(PyObject *self, void *closure) +{ + return PyInt_FromLong(SHA_DIGESTSIZE); +} + +static PyObject * +SHA_get_name(PyObject *self, void *closure) +{ + return PyString_FromStringAndSize("SHA1", 4); +} + +static PyGetSetDef SHA_getseters[] = { + {"digest_size", + (getter)SHA_get_digest_size, NULL, + NULL, + NULL}, + {"block_size", + (getter)SHA_get_block_size, NULL, + NULL, + NULL}, + {"name", + (getter)SHA_get_name, NULL, + NULL, + NULL}, + /* the old md5 and sha modules support 'digest_size' as in PEP 247. + * the old sha module also supported 'digestsize'. ugh. */ + {"digestsize", + (getter)SHA_get_digest_size, NULL, + NULL, + NULL}, + {NULL} /* Sentinel */ +}; + +static PyTypeObject SHAtype = { + PyObject_HEAD_INIT(NULL) + 0, /*ob_size*/ + "_sha.sha", /*tp_name*/ + sizeof(SHAobject), /*tp_size*/ + 0, /*tp_itemsize*/ + /* methods */ + SHA_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_compare*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + 0, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + SHA_methods, /* tp_methods */ + 0, /* tp_members */ + SHA_getseters, /* tp_getset */ +}; + + +/* The single module-level function: new() */ + +PyDoc_STRVAR(SHA_new__doc__, +"Return a new SHA hashing object. An optional string argument\n\ +may be provided; if present, this string will be automatically\n\ +hashed."); + +static PyObject * +SHA_new(PyObject *self, PyObject *args, PyObject *kwdict) +{ + static char *kwlist[] = {"string", NULL}; + SHAobject *new; + unsigned char *cp = NULL; + int len; + + if (!PyArg_ParseTupleAndKeywords(args, kwdict, "|s#:new", kwlist, + &cp, &len)) { + return NULL; + } + + if ((new = newSHAobject()) == NULL) + return NULL; + + sha_init(new); + + if (PyErr_Occurred()) { + Py_DECREF(new); + return NULL; + } + if (cp) + sha_update(new, cp, len); + + return (PyObject *)new; +} + + +/* List of functions exported by this module */ + +static struct PyMethodDef SHA_functions[] = { + {"new", (PyCFunction)SHA_new, METH_VARARGS|METH_KEYWORDS, SHA_new__doc__}, + {NULL, NULL} /* Sentinel */ +}; + + +/* Initialize this module. */ + +#define insint(n,v) { PyModule_AddIntConstant(m,n,v); } + +PyMODINIT_FUNC +init_sha(void) +{ + PyObject *m; + + SHAtype.ob_type = &PyType_Type; + if (PyType_Ready(&SHAtype) < 0) + return; + m = Py_InitModule("_sha", SHA_functions); + if (m == NULL) + return; + + /* Add some symbolic constants to the module */ + insint("blocksize", 1); /* For future use, in case some hash + functions require an integral number of + blocks */ + insint("digestsize", 20); + insint("digest_size", 20); +} diff --git a/src/README.txt b/src/README.txt --- a/src/README.txt +++ b/src/README.txt @@ -2,7 +2,7 @@ Distutils2 ========== -Welcome to Distutils2 ! +Welcome to Distutils2! Distutils2 is the new version of Distutils. It's not backward compatible with Distutils but provides more features, and implement most new packaging @@ -10,6 +10,6 @@ See the documentation at http://packages.python.org/Distutils2 for more info. -**Beware that Distutils2 is its in early stage and should not be used in +**Beware that Distutils2 is in its early stage and should not be used in production. Its API is subject to changes** diff --git a/src/distutils2/README b/src/distutils2/README --- a/src/distutils2/README +++ b/src/distutils2/README @@ -1,4 +1,4 @@ -This directory contains the Distutils package. +This directory contains the Distutils2 package. There's a full documentation available at: @@ -8,6 +8,6 @@ http://www.python.org/sigs/distutils-sig/ -WARNING: Distutils2 must remain compatible with 2.4 +WARNING: Distutils2 must remain compatible with Python 2.4 $Id: README 70017 2009-02-27 12:53:34Z tarek.ziade $ diff --git a/src/distutils2/__init__.py b/src/distutils2/__init__.py --- a/src/distutils2/__init__.py +++ b/src/distutils2/__init__.py @@ -1,16 +1,26 @@ """distutils -The main package for the Python Module Distribution Utilities. Normally -used from a setup script as +The main package for the Python Distribution Utilities 2. Setup +scripts should import the setup function from distutils2.core: - from distutils.core import setup + from distutils2.core import setup - setup (...) + setup(name=..., version=..., ...) + +Third-party tools can use parts of Distutils2 as building blocks +without causing the other modules to be imported: + + import distutils2.version + import distutils2.pypi.simple + import distutils2.tests.pypi_server """ -__all__ = ['__version__', 'setup'] +__all__ = ['__version__'] __revision__ = "$Id: __init__.py 78020 2010-02-06 16:37:32Z benjamin.peterson $" __version__ = "1.0a2" -from distutils2.core import setup +# when set to True, converts doctests by default too +run_2to3_on_doctests = True +# Standard package names for fixer packages +lib2to3_fixer_packages = ['lib2to3.fixes'] diff --git a/src/distutils2/_backport/hashlib.py b/src/distutils2/_backport/hashlib.py new file mode 100644 --- /dev/null +++ b/src/distutils2/_backport/hashlib.py @@ -0,0 +1,143 @@ +# $Id$ +# +# Copyright (C) 2005 Gregory P. Smith (greg at krypto.org) +# Licensed to PSF under a Contributor Agreement. +# + +__doc__ = """hashlib module - A common interface to many hash functions. + +new(name, string='') - returns a new hash object implementing the + given hash function; initializing the hash + using the given string data. + +Named constructor functions are also available, these are much faster +than using new(): + +md5(), sha1(), sha224(), sha256(), sha384(), and sha512() + +More algorithms may be available on your platform but the above are +guaranteed to exist. + +NOTE: If you want the adler32 or crc32 hash functions they are available in +the zlib module. + +Choose your hash function wisely. Some have known collision weaknesses. +sha384 and sha512 will be slow on 32 bit platforms. + +Hash objects have these methods: + - update(arg): Update the hash object with the string arg. Repeated calls + are equivalent to a single call with the concatenation of all + the arguments. + - digest(): Return the digest of the strings passed to the update() method + so far. This may contain non-ASCII characters, including + NUL bytes. + - hexdigest(): Like digest() except the digest is returned as a string of + double length, containing only hexadecimal digits. + - copy(): Return a copy (clone) of the hash object. This can be used to + efficiently compute the digests of strings that share a common + initial substring. + +For example, to obtain the digest of the string 'Nobody inspects the +spammish repetition': + + >>> import hashlib + >>> m = hashlib.md5() + >>> m.update("Nobody inspects") + >>> m.update(" the spammish repetition") + >>> m.digest() + '\\xbbd\\x9c\\x83\\xdd\\x1e\\xa5\\xc9\\xd9\\xde\\xc9\\xa1\\x8d\\xf0\\xff\\xe9' + +More condensed: + + >>> hashlib.sha224("Nobody inspects the spammish repetition").hexdigest() + 'a4337bc45a8fc544c03f52dc550cd6e1e87021bc896588bd79e901e2' + +""" + +# This tuple and __get_builtin_constructor() must be modified if a new +# always available algorithm is added. +__always_supported = ('md5', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512') + +algorithms = __always_supported + +__all__ = __always_supported + ('new', 'algorithms') + + +def __get_builtin_constructor(name): + if name in ('SHA1', 'sha1'): + import _sha + return _sha.new + elif name in ('MD5', 'md5'): + import _md5 + return _md5.new + elif name in ('SHA256', 'sha256', 'SHA224', 'sha224'): + import _sha256 + bs = name[3:] + if bs == '256': + return _sha256.sha256 + elif bs == '224': + return _sha256.sha224 + elif name in ('SHA512', 'sha512', 'SHA384', 'sha384'): + import _sha512 + bs = name[3:] + if bs == '512': + return _sha512.sha512 + elif bs == '384': + return _sha512.sha384 + + raise ValueError('unsupported hash type %s' % name) + + +def __get_openssl_constructor(name): + try: + f = getattr(_hashlib, 'openssl_' + name) + # Allow the C module to raise ValueError. The function will be + # defined but the hash not actually available thanks to OpenSSL. + f() + # Use the C function directly (very fast) + return f + except (AttributeError, ValueError): + return __get_builtin_constructor(name) + + +def __py_new(name, string=''): + """new(name, string='') - Return a new hashing object using the named algorithm; + optionally initialized with a string. + """ + return __get_builtin_constructor(name)(string) + + +def __hash_new(name, string=''): + """new(name, string='') - Return a new hashing object using the named algorithm; + optionally initialized with a string. + """ + try: + return _hashlib.new(name, string) + except ValueError: + # If the _hashlib module (OpenSSL) doesn't support the named + # hash, try using our builtin implementations. + # This allows for SHA224/256 and SHA384/512 support even though + # the OpenSSL library prior to 0.9.8 doesn't provide them. + return __get_builtin_constructor(name)(string) + + +try: + import _hashlib + new = __hash_new + __get_hash = __get_openssl_constructor +except ImportError: + new = __py_new + __get_hash = __get_builtin_constructor + +for __func_name in __always_supported: + # try them all, some may not work due to the OpenSSL + # version not supporting that algorithm. + try: + globals()[__func_name] = __get_hash(__func_name) + except ValueError: + import logging + logging.exception('code for hash %s was not found.', __func_name) + +# Cleanup locals() +del __always_supported, __func_name, __get_hash +del __py_new, __hash_new, __get_openssl_constructor diff --git a/src/distutils2/_backport/pkgutil.py b/src/distutils2/_backport/pkgutil.py --- a/src/distutils2/_backport/pkgutil.py +++ b/src/distutils2/_backport/pkgutil.py @@ -1,4 +1,4 @@ -""" Utilities to support packages. """ +"""Utilities to support packages.""" # NOTE: This module must remain compatible with Python 2.3, as it is shared # by setuptools for distribution with Python 2.3 and up. @@ -181,7 +181,7 @@ iter_importer_modules = simplegeneric(iter_importer_modules) -class ImpImporter: +class ImpImporter(object): """:pep:`302` Importer that wraps Python's "classic" import algorithm ``ImpImporter(dirname)`` produces a :pep:`302` importer that searches that @@ -244,7 +244,7 @@ yield prefix + modname, ispkg -class ImpLoader: +class ImpLoader(object): """:pep:`302` Loader that wraps Python's "classic" import algorithm """ code = source = None @@ -827,6 +827,9 @@ def __eq__(self, other): return isinstance(other, Distribution) and self.path == other.path + # See http://docs.python.org/reference/datamodel#object.__hash__ + __hash__ = object.__hash__ + class EggInfoDistribution(object): """Created with the *path* of the ``.egg-info`` directory or file provided @@ -950,6 +953,9 @@ return isinstance(other, EggInfoDistribution) and \ self.path == other.path + # See http://docs.python.org/reference/datamodel#object.__hash__ + __hash__ = object.__hash__ + def _normalize_dist_name(name): """Returns a normalized name from the given *name*. @@ -1021,7 +1027,7 @@ returned if one is found that has metadata that matches *name* for the *name* metadata field. - This function only returns the first result founded, as no more than one + This function only returns the first result found, as no more than one value is expected. If the directory is not found, ``None`` is returned. :rtype: :class:`Distribution` or :class:`EggInfoDistribution` or None""" @@ -1081,7 +1087,7 @@ then all files and directories ending with ``.egg-info`` are considered as well and returns an :class:`EggInfoDistribution` instance. - This function only returns the first result founded, since no more than + This function only returns the first result found, since no more than one values are expected. If the directory is not found, returns ``None``. :parameter version: a version specifier that indicates the version diff --git a/src/distutils2/_backport/sysconfig.cfg b/src/distutils2/_backport/sysconfig.cfg --- a/src/distutils2/_backport/sysconfig.cfg +++ b/src/distutils2/_backport/sysconfig.cfg @@ -1,6 +1,6 @@ [globals] # These are the useful categories that are sometimes referenced at runtime, -# using pkgutils.open(): +# using pkgutil.open(): config = {confdir}/{distribution.name} # Configuration files appdata = {datadir}/{distribution.name} # Non-writable data that is independent of architecture (images, many xml/text files) appdata.arch = {libdir}/{distribution.name} # Non-writable data that is architecture-dependent (some binary data formats) diff --git a/src/distutils2/_backport/sysconfig.py b/src/distutils2/_backport/sysconfig.py --- a/src/distutils2/_backport/sysconfig.py +++ b/src/distutils2/_backport/sysconfig.py @@ -5,7 +5,7 @@ import os import re from os.path import pardir, abspath -from ConfigParser import ConfigParser +from ConfigParser import RawConfigParser _PREFIX = os.path.normpath(sys.prefix) _EXEC_PREFIX = os.path.normpath(sys.exec_prefix) @@ -14,7 +14,7 @@ # XXX _CONFIG_DIR will be set by the Makefile later _CONFIG_DIR = os.path.normpath(os.path.dirname(__file__)) _CONFIG_FILE = os.path.join(_CONFIG_DIR, 'sysconfig.cfg') -_SCHEMES = ConfigParser() +_SCHEMES = RawConfigParser() _SCHEMES.read(_CONFIG_FILE) _VAR_REPL = re.compile(r'\{([^{]*?)\}') @@ -580,10 +580,11 @@ # behaviour. pass else: - m = re.search( - r'ProductUserVisibleVersion\s*' + - r'(.*?)', f.read()) - f.close() + try: + m = re.search(r'ProductUserVisibleVersion\s*' + r'(.*?)', f.read()) + finally: + f.close() if m is not None: macrelease = '.'.join(m.group(1).split('.')[:2]) # else: fall back to the default behaviour diff --git a/src/distutils2/_backport/tarfile.py b/src/distutils2/_backport/tarfile.py --- a/src/distutils2/_backport/tarfile.py +++ b/src/distutils2/_backport/tarfile.py @@ -370,7 +370,7 @@ #--------------------------- # internal stream interface #--------------------------- -class _LowLevelFile: +class _LowLevelFile(object): """Low-level file object. Supports reading and writing. It is used instead of a regular file object for streaming access. @@ -394,7 +394,7 @@ def write(self, s): os.write(self.fd, s) -class _Stream: +class _Stream(object): """Class that serves as an adapter between TarFile and a stream-like object. The stream-like object only needs to have a read() or write() method and is accessed @@ -2003,8 +2003,10 @@ # Append the tar header and data to the archive. if tarinfo.isreg(): f = bltn_open(name, "rb") - self.addfile(tarinfo, f) - f.close() + try: + self.addfile(tarinfo, f) + finally: + f.close() elif tarinfo.isdir(): self.addfile(tarinfo) @@ -2214,9 +2216,11 @@ """ source = self.extractfile(tarinfo) target = bltn_open(targetpath, "wb") - copyfileobj(source, target) - source.close() - target.close() + try: + copyfileobj(source, target) + finally: + source.close() + target.close() def makeunknown(self, tarinfo, targetpath): """Make a file from a TarInfo object with an unknown type @@ -2423,7 +2427,7 @@ print >> sys.stderr, msg # class TarFile -class TarIter: +class TarIter(object): """Iterator Class. for tarinfo in TarFile(...): @@ -2460,7 +2464,7 @@ return tarinfo # Helper classes for sparse file support -class _section: +class _section(object): """Base class for _data and _hole. """ def __init__(self, offset, size): @@ -2507,7 +2511,7 @@ #--------------------------------------------- TAR_PLAIN = 0 # zipfile.ZIP_STORED TAR_GZIPPED = 8 # zipfile.ZIP_DEFLATED -class TarFileCompat: +class TarFileCompat(object): """TarFile class compatible with standard module zipfile's ZipFile class. """ @@ -2564,8 +2568,10 @@ are able to handle, else return False. """ try: - t = open(name) - t.close() + try: + t = open(name) + finally: + t.close() return True except TarError: return False diff --git a/src/distutils2/_backport/tests/test_pkgutil.py b/src/distutils2/_backport/tests/test_pkgutil.py --- a/src/distutils2/_backport/tests/test_pkgutil.py +++ b/src/distutils2/_backport/tests/test_pkgutil.py @@ -1,6 +1,5 @@ # -*- coding: utf-8 -*- """Tests for PEP 376 pkgutil functionality""" -import unittest2 import sys import os import csv @@ -14,22 +13,33 @@ from md5 import md5 from test.test_support import run_unittest, TESTFN +from distutils2.tests.support import unittest from distutils2._backport import pkgutil +try: + from os.path import relpath +except ImportError: + try: + from unittest.compatibility import relpath + except ImportError: + from unittest2.compatibility import relpath + # TODO Add a test for getting a distribution that is provided by another -# distribution. +# distribution. # TODO Add a test for absolute pathed RECORD items (e.g. /etc/myapp/config.ini) # Adapted from Python 2.7's trunk -class TestPkgUtilData(unittest2.TestCase): +class TestPkgUtilData(unittest.TestCase): def setUp(self): + super(TestPkgUtilData, self).setUp() self.dirname = tempfile.mkdtemp() sys.path.insert(0, self.dirname) def tearDown(self): + super(TestPkgUtilData, self).tearDown() del sys.path[0] shutil.rmtree(self.dirname) @@ -44,15 +54,22 @@ os.mkdir(package_dir) # Empty init.py f = open(os.path.join(package_dir, '__init__.py'), "wb") - f.close() + try: + pass + finally: + f.close() # Resource files, res.txt, sub/res.txt f = open(os.path.join(package_dir, 'res.txt'), "wb") - f.write(RESOURCE_DATA) - f.close() + try: + f.write(RESOURCE_DATA) + finally: + f.close() os.mkdir(os.path.join(package_dir, 'sub')) f = open(os.path.join(package_dir, 'sub', 'res.txt'), "wb") - f.write(RESOURCE_DATA) - f.close() + try: + f.write(RESOURCE_DATA) + finally: + f.close() # Check we can read the resources res1 = pkgutil.get_data(pkg, 'res.txt') @@ -72,13 +89,14 @@ # Make a package with some resources zip_file = os.path.join(self.dirname, zip) z = zipfile.ZipFile(zip_file, 'w') - - # Empty init.py - z.writestr(pkg + '/__init__.py', "") - # Resource files, res.txt, sub/res.txt - z.writestr(pkg + '/res.txt', RESOURCE_DATA) - z.writestr(pkg + '/sub/res.txt', RESOURCE_DATA) - z.close() + try: + # Empty init.py + z.writestr(pkg + '/__init__.py', "") + # Resource files, res.txt, sub/res.txt + z.writestr(pkg + '/res.txt', RESOURCE_DATA) + z.writestr(pkg + '/sub/res.txt', RESOURCE_DATA) + finally: + z.close() # Check we can read the resources sys.path.insert(0, zip_file) @@ -91,7 +109,7 @@ del sys.modules[pkg] # Adapted from Python 2.7's trunk -class TestPkgUtilPEP302(unittest2.TestCase): +class TestPkgUtilPEP302(unittest.TestCase): class MyTestLoader(object): def load_module(self, fullname): @@ -113,10 +131,12 @@ return TestPkgUtilPEP302.MyTestLoader() def setUp(self): + super(TestPkgUtilPEP302, self).setUp() sys.meta_path.insert(0, self.MyTestImporter()) def tearDown(self): del sys.meta_path[0] + super(TestPkgUtilPEP302, self).tearDown() def test_getdata_pep302(self): # Use a dummy importer/loader @@ -134,10 +154,11 @@ del sys.modules['foo'] -class TestPkgUtilDistribution(unittest2.TestCase): - """Tests the pkgutil.Distribution class""" +class TestPkgUtilDistribution(unittest.TestCase): + # Tests the pkgutil.Distribution class def setUp(self): + super(TestPkgUtilDistribution, self).setUp() self.fake_dists_path = os.path.abspath( os.path.join(os.path.dirname(__file__), 'fake_dists')) @@ -151,7 +172,7 @@ return md5_hash.hexdigest() def record_pieces(file): - path = os.path.relpath(file, sys.prefix) + path = relpath(file, sys.prefix) digest = get_hexdigest(file) size = os.path.getsize(file) return [path, digest, size] @@ -171,7 +192,7 @@ for file in ['INSTALLER', 'METADATA', 'REQUESTED']: record_writer.writerow(record_pieces( os.path.join(distinfo_dir, file))) - record_writer.writerow([os.path.relpath(record_file, sys.prefix)]) + record_writer.writerow([relpath(record_file, sys.prefix)]) del record_writer # causes the RECORD file to close record_reader = csv.reader(open(record_file, 'rb')) record_data = [] @@ -186,10 +207,11 @@ for distinfo_dir in self.distinfo_dirs: record_file = os.path.join(distinfo_dir, 'RECORD') open(record_file, 'w').close() + super(TestPkgUtilDistribution, self).tearDown() def test_instantiation(self): - """Test the Distribution class's instantiation provides us with usable - attributes.""" + # Test the Distribution class's instantiation provides us with usable + # attributes. # Import the Distribution class from distutils2._backport.pkgutil import distinfo_dirname, Distribution @@ -207,7 +229,7 @@ self.assertTrue(isinstance(dist.requested, type(bool()))) def test_installed_files(self): - """Test the iteration of installed files.""" + # Test the iteration of installed files. # Test the distribution's installed files from distutils2._backport.pkgutil import Distribution for distinfo_dir in self.distinfo_dirs: @@ -219,17 +241,17 @@ self.assertEqual(size, record_data[path][1]) def test_uses(self): - """Test to determine if a distribution uses a specified file.""" + # Test to determine if a distribution uses a specified file. # Criteria to test against distinfo_name = 'grammar-1.0a4' distinfo_dir = os.path.join(self.fake_dists_path, distinfo_name + '.dist-info') true_path = [self.fake_dists_path, distinfo_name, \ 'grammar', 'utils.py'] - true_path = os.path.relpath(os.path.join(*true_path), sys.prefix) + true_path = relpath(os.path.join(*true_path), sys.prefix) false_path = [self.fake_dists_path, 'towel_stuff-0.1', 'towel_stuff', '__init__.py'] - false_path = os.path.relpath(os.path.join(*false_path), sys.prefix) + false_path = relpath(os.path.join(*false_path), sys.prefix) # Test if the distribution uses the file in question from distutils2._backport.pkgutil import Distribution @@ -238,7 +260,7 @@ self.assertFalse(dist.uses(false_path)) def test_get_distinfo_file(self): - """Test the retrieval of dist-info file objects.""" + # Test the retrieval of dist-info file objects. from distutils2._backport.pkgutil import Distribution distinfo_name = 'choxie-2.0.0.9' other_distinfo_name = 'grammar-1.0a4' @@ -271,7 +293,7 @@ 'ENTRYPOINTS') def test_get_distinfo_files(self): - """Test for the iteration of RECORD path entries.""" + # Test for the iteration of RECORD path entries. from distutils2._backport.pkgutil import Distribution distinfo_name = 'towel_stuff-0.1' distinfo_dir = os.path.join(self.fake_dists_path, @@ -288,10 +310,11 @@ self.assertEqual(sorted(found), sorted(distinfo_record_paths)) -class TestPkgUtilPEP376(unittest2.TestCase): - """Tests for the new functionality added in PEP 376.""" +class TestPkgUtilPEP376(unittest.TestCase): + # Tests for the new functionality added in PEP 376. def setUp(self): + super(TestPkgUtilPEP376, self).setUp() # Setup the path environment with our fake distributions current_path = os.path.abspath(os.path.dirname(__file__)) self.sys_path = sys.path[:] @@ -300,17 +323,18 @@ def tearDown(self): sys.path[:] = self.sys_path + super(TestPkgUtilPEP376, self).tearDown() def test_distinfo_dirname(self): - """Given a name and a version, we expect the distinfo_dirname function - to return a standard distribution information directory name.""" + # Given a name and a version, we expect the distinfo_dirname function + # to return a standard distribution information directory name. items = [# (name, version, standard_dirname) # Test for a very simple single word name and decimal # version number ('docutils', '0.5', 'docutils-0.5.dist-info'), # Test for another except this time with a '-' in the name, which - # needs to be transformed during the name lookup + # needs to be transformed during the name lookup ('python-ldap', '2.5', 'python_ldap-2.5.dist-info'), # Test for both '-' in the name and a funky version number ('python-ldap', '2.5 a---5', 'python_ldap-2.5 a---5.dist-info'), @@ -325,7 +349,7 @@ self.assertEqual(dirname, standard_dirname) def test_get_distributions(self): - """Lookup all distributions found in the ``sys.path``.""" + # Lookup all distributions found in the ``sys.path``. # This test could potentially pick up other installed distributions fake_dists = [('grammar', '1.0a4'), ('choxie', '2.0.0.9'), ('towel-stuff', '0.1')] @@ -366,7 +390,7 @@ self.assertListEqual(sorted(fake_dists), sorted(found_dists)) def test_get_distribution(self): - """Test for looking up a distribution by name.""" + # Test for looking up a distribution by name. # Test the lookup of the towel-stuff distribution name = 'towel-stuff' # Note: This is different from the directory name @@ -412,7 +436,7 @@ self.assertEqual(dist.name, 'strawberry') def test_get_file_users(self): - """Test the iteration of distributions that use a file.""" + # Test the iteration of distributions that use a file. from distutils2._backport.pkgutil import get_file_users, Distribution name = 'towel_stuff-0.1' path = os.path.join(self.fake_dists_path, name, @@ -422,7 +446,7 @@ self.assertEqual(dist.name, name) def test_provides(self): - """ Test for looking up distributions by what they provide """ + # Test for looking up distributions by what they provide from distutils2._backport.pkgutil import provides_distribution from distutils2.errors import DistutilsError @@ -494,7 +518,7 @@ checkLists(l, []) def test_obsoletes(self): - """ Test looking for distributions based on what they obsolete """ + # Test looking for distributions based on what they obsolete from distutils2._backport.pkgutil import obsoletes_distribution from distutils2.errors import DistutilsError @@ -527,12 +551,12 @@ def test_suite(): - suite = unittest2.TestSuite() - testcase_loader = unittest2.loader.defaultTestLoader.loadTestsFromTestCase - suite.addTest(testcase_loader(TestPkgUtilData)) - suite.addTest(testcase_loader(TestPkgUtilDistribution)) - suite.addTest(testcase_loader(TestPkgUtilPEP302)) - suite.addTest(testcase_loader(TestPkgUtilPEP376)) + suite = unittest.TestSuite() + load = unittest.defaultTestLoader.loadTestsFromTestCase + suite.addTest(load(TestPkgUtilData)) + suite.addTest(load(TestPkgUtilDistribution)) + suite.addTest(load(TestPkgUtilPEP302)) + suite.addTest(load(TestPkgUtilPEP376)) return suite diff --git a/src/distutils2/_backport/tests/test_sysconfig.py b/src/distutils2/_backport/tests/test_sysconfig.py --- a/src/distutils2/_backport/tests/test_sysconfig.py +++ b/src/distutils2/_backport/tests/test_sysconfig.py @@ -8,7 +8,7 @@ import os import shutil from copy import copy, deepcopy -from ConfigParser import ConfigParser +from ConfigParser import RawConfigParser from test.test_support import run_unittest, TESTFN @@ -88,15 +88,13 @@ shutil.rmtree(path) def test_nested_var_substitution(self): - """Assert that the {curly brace token} expansion pattern will replace - only the inner {something} on nested expressions like {py{something}} on - the first pass. + # Assert that the {curly brace token} expansion pattern will replace + # only the inner {something} on nested expressions like {py{something}} on + # the first pass. - We have no plans to make use of this, but it keeps the option open for - the future, at the cost only of disallowing { itself as a piece of a - substitution key (which would be weird). - - """ + # We have no plans to make use of this, but it keeps the option open for + # the future, at the cost only of disallowing { itself as a piece of a + # substitution key (which would be weird). self.assertEqual(_subst_vars('{py{version}}', {'version': '31'}), '{py31}') def test_get_paths(self): @@ -107,7 +105,7 @@ wanted.sort() scheme = scheme.items() scheme.sort() - self.assertEquals(scheme, wanted) + self.assertEqual(scheme, wanted) def test_get_config_vars(self): cvars = get_config_vars() @@ -120,21 +118,21 @@ sys.version = ('2.4.4 (#71, Oct 18 2006, 08:34:43) ' '[MSC v.1310 32 bit (Intel)]') sys.platform = 'win32' - self.assertEquals(get_platform(), 'win32') + self.assertEqual(get_platform(), 'win32') # windows XP, amd64 os.name = 'nt' sys.version = ('2.4.4 (#71, Oct 18 2006, 08:34:43) ' '[MSC v.1310 32 bit (Amd64)]') sys.platform = 'win32' - self.assertEquals(get_platform(), 'win-amd64') + self.assertEqual(get_platform(), 'win-amd64') # windows XP, itanium os.name = 'nt' sys.version = ('2.4.4 (#71, Oct 18 2006, 08:34:43) ' '[MSC v.1310 32 bit (Itanium)]') sys.platform = 'win32' - self.assertEquals(get_platform(), 'win-ia64') + self.assertEqual(get_platform(), 'win-ia64') # macbook os.name = 'posix' @@ -153,9 +151,9 @@ maxint = sys.maxint try: sys.maxint = 2147483647 - self.assertEquals(get_platform(), 'macosx-10.3-ppc') + self.assertEqual(get_platform(), 'macosx-10.3-ppc') sys.maxint = 9223372036854775807 - self.assertEquals(get_platform(), 'macosx-10.3-ppc64') + self.assertEqual(get_platform(), 'macosx-10.3-ppc64') finally: sys.maxint = maxint @@ -173,9 +171,9 @@ maxint = sys.maxint try: sys.maxint = 2147483647 - self.assertEquals(get_platform(), 'macosx-10.3-i386') + self.assertEqual(get_platform(), 'macosx-10.3-i386') sys.maxint = 9223372036854775807 - self.assertEquals(get_platform(), 'macosx-10.3-x86_64') + self.assertEqual(get_platform(), 'macosx-10.3-x86_64') finally: sys.maxint = maxint @@ -186,33 +184,33 @@ '-fno-strict-aliasing -fno-common ' '-dynamic -DNDEBUG -g -O3') - self.assertEquals(get_platform(), 'macosx-10.4-fat') + self.assertEqual(get_platform(), 'macosx-10.4-fat') get_config_vars()['CFLAGS'] = ('-arch x86_64 -arch i386 -isysroot ' '/Developer/SDKs/MacOSX10.4u.sdk ' '-fno-strict-aliasing -fno-common ' '-dynamic -DNDEBUG -g -O3') - self.assertEquals(get_platform(), 'macosx-10.4-intel') + self.assertEqual(get_platform(), 'macosx-10.4-intel') get_config_vars()['CFLAGS'] = ('-arch x86_64 -arch ppc -arch i386 -isysroot ' '/Developer/SDKs/MacOSX10.4u.sdk ' '-fno-strict-aliasing -fno-common ' '-dynamic -DNDEBUG -g -O3') - self.assertEquals(get_platform(), 'macosx-10.4-fat3') + self.assertEqual(get_platform(), 'macosx-10.4-fat3') get_config_vars()['CFLAGS'] = ('-arch ppc64 -arch x86_64 -arch ppc -arch i386 -isysroot ' '/Developer/SDKs/MacOSX10.4u.sdk ' '-fno-strict-aliasing -fno-common ' '-dynamic -DNDEBUG -g -O3') - self.assertEquals(get_platform(), 'macosx-10.4-universal') + self.assertEqual(get_platform(), 'macosx-10.4-universal') get_config_vars()['CFLAGS'] = ('-arch x86_64 -arch ppc64 -isysroot ' '/Developer/SDKs/MacOSX10.4u.sdk ' '-fno-strict-aliasing -fno-common ' '-dynamic -DNDEBUG -g -O3') - self.assertEquals(get_platform(), 'macosx-10.4-fat64') + self.assertEqual(get_platform(), 'macosx-10.4-fat64') for arch in ('ppc', 'i386', 'x86_64', 'ppc64'): get_config_vars()['CFLAGS'] = ('-arch %s -isysroot ' @@ -220,7 +218,7 @@ '-fno-strict-aliasing -fno-common ' '-dynamic -DNDEBUG -g -O3'%(arch,)) - self.assertEquals(get_platform(), 'macosx-10.4-%s'%(arch,)) + self.assertEqual(get_platform(), 'macosx-10.4-%s'%(arch,)) # linux debian sarge os.name = 'posix' @@ -230,7 +228,7 @@ self._set_uname(('Linux', 'aglae', '2.6.21.1dedibox-r7', '#1 Mon Apr 30 17:25:38 CEST 2007', 'i686')) - self.assertEquals(get_platform(), 'linux-i686') + self.assertEqual(get_platform(), 'linux-i686') # XXX more platforms to tests here @@ -241,11 +239,11 @@ def test_get_scheme_names(self): wanted = ('nt', 'nt_user', 'os2', 'os2_home', 'posix_home', 'posix_prefix', 'posix_user') - self.assertEquals(get_scheme_names(), wanted) + self.assertEqual(get_scheme_names(), wanted) def test_expand_globals(self): - config = ConfigParser() + config = RawConfigParser() config.add_section('globals') config.set('globals', 'foo', 'ok') config.add_section('posix') @@ -254,8 +252,8 @@ _expand_globals(config) - self.assertEquals(config.get('posix', 'foo'), 'ok') - self.assertEquals(config.get('posix', 'more'), '/etc/ok') + self.assertEqual(config.get('posix', 'foo'), 'ok') + self.assertEqual(config.get('posix', 'more'), '/etc/ok') # we might not have globals after all # extending again (==no more globals section) diff --git a/src/distutils2/command/bdist.py b/src/distutils2/command/bdist.py --- a/src/distutils2/command/bdist.py +++ b/src/distutils2/command/bdist.py @@ -62,7 +62,7 @@ 'os2': 'zip'} # Establish the preferred order (for the --help-formats option). - format_commands = ['rpm', 'gztar', 'bztar', 'ztar', 'tar', + format_commands = ['gztar', 'bztar', 'ztar', 'tar', 'wininst', 'zip', 'msi'] # And the real information. @@ -96,7 +96,7 @@ # 'bdist_base' -- parent of per-built-distribution-format # temporary directories (eg. we'll probably have - # "build/bdist./dumb", "build/bdist./rpm", etc.) + # "build/bdist./dumb", etc.) if self.bdist_base is None: build_base = self.get_finalized_command('build').build_base self.bdist_base = os.path.join(build_base, @@ -126,7 +126,7 @@ # Reinitialize and run each command. for i in range(len(self.formats)): cmd_name = commands[i] - sub_cmd = self.reinitialize_command(cmd_name) + sub_cmd = self.get_reinitialized_command(cmd_name) # passing the owner and group names for tar archiving if cmd_name == 'bdist_dumb': diff --git a/src/distutils2/command/bdist_dumb.py b/src/distutils2/command/bdist_dumb.py --- a/src/distutils2/command/bdist_dumb.py +++ b/src/distutils2/command/bdist_dumb.py @@ -85,7 +85,7 @@ if not self.skip_build: self.run_command('build') - install = self.reinitialize_command('install', reinit_subcommands=1) + install = self.get_reinitialized_command('install', reinit_subcommands=1) install.root = self.bdist_dir install.skip_build = self.skip_build install.warn_dir = 0 diff --git a/src/distutils2/command/bdist_msi.py b/src/distutils2/command/bdist_msi.py --- a/src/distutils2/command/bdist_msi.py +++ b/src/distutils2/command/bdist_msi.py @@ -177,12 +177,12 @@ if not self.skip_build: self.run_command('build') - install = self.reinitialize_command('install', reinit_subcommands=1) + install = self.get_reinitialized_command('install', reinit_subcommands=1) install.prefix = self.bdist_dir install.skip_build = self.skip_build install.warn_dir = 0 - install_lib = self.reinitialize_command('install_lib') + install_lib = self.get_reinitialized_command('install_lib') # we do not want to include pyc or pyo files install_lib.compile = 0 install_lib.optimize = 0 diff --git a/src/distutils2/command/bdist_wininst.py b/src/distutils2/command/bdist_wininst.py --- a/src/distutils2/command/bdist_wininst.py +++ b/src/distutils2/command/bdist_wininst.py @@ -126,13 +126,13 @@ if not self.skip_build: self.run_command('build') - install = self.reinitialize_command('install', reinit_subcommands=1) + install = self.get_reinitialized_command('install', reinit_subcommands=1) install.root = self.bdist_dir install.skip_build = self.skip_build install.warn_dir = 0 install.plat_name = self.plat_name - install_lib = self.reinitialize_command('install_lib') + install_lib = self.get_reinitialized_command('install_lib') # we do not want to include pyc or pyo files install_lib.compile = 0 install_lib.optimize = 0 diff --git a/src/distutils2/command/build_ext.py b/src/distutils2/command/build_ext.py --- a/src/distutils2/command/build_ext.py +++ b/src/distutils2/command/build_ext.py @@ -188,7 +188,24 @@ if self.package is None: self.package = self.distribution.ext_package + # Ensure that the list of extensions is valid, i.e. it is a list of + # Extension objects. self.extensions = self.distribution.ext_modules + if self.extensions: + if not isinstance(self.extensions, (list, tuple)): + type_name = (self.extensions is None and 'None' + or type(self.extensions).__name__) + raise DistutilsSetupError( + "'ext_modules' must be a sequence of Extension instances," + " not %s" % (type_name,)) + for i, ext in enumerate(self.extensions): + if isinstance(ext, Extension): + continue # OK! (assume type-checking done + # by Extension constructor) + type_name = (ext is None and 'None' or type(ext).__name__) + raise DistutilsSetupError( + "'ext_modules' item %d must be an Extension instance," + " not %s" % (i, type_name)) # Make sure Python's include directories (for Python.h, pyconfig.h, # etc.) are in the include search path. @@ -396,86 +413,7 @@ # Now actually compile and link everything. self.build_extensions() - def check_extensions_list(self, extensions): - """Ensure that the list of extensions (presumably provided as a - command option 'extensions') is valid, i.e. it is a list of - Extension objects. We also support the old-style list of 2-tuples, - where the tuples are (ext_name, build_info), which are converted to - Extension instances here. - - Raise DistutilsSetupError if the structure is invalid anywhere; - just returns otherwise. - """ - if not isinstance(extensions, list): - raise DistutilsSetupError, \ - "'ext_modules' option must be a list of Extension instances" - - for i, ext in enumerate(extensions): - if isinstance(ext, Extension): - continue # OK! (assume type-checking done - # by Extension constructor) - - if not isinstance(ext, tuple) or len(ext) != 2: - raise DistutilsSetupError, \ - ("each element of 'ext_modules' option must be an " - "Extension instance or 2-tuple") - - ext_name, build_info = ext - - log.warn(("old-style (ext_name, build_info) tuple found in " - "ext_modules for extension '%s'" - "-- please convert to Extension instance" % ext_name)) - - if not (isinstance(ext_name, str) and - extension_name_re.match(ext_name)): - raise DistutilsSetupError, \ - ("first element of each tuple in 'ext_modules' " - "must be the extension name (a string)") - - if not isinstance(build_info, dict): - raise DistutilsSetupError, \ - ("second element of each tuple in 'ext_modules' " - "must be a dictionary (build info)") - - # OK, the (ext_name, build_info) dict is type-safe: convert it - # to an Extension instance. - ext = Extension(ext_name, build_info['sources']) - - # Easy stuff: one-to-one mapping from dict elements to - # instance attributes. - for key in ('include_dirs', 'library_dirs', 'libraries', - 'extra_objects', 'extra_compile_args', - 'extra_link_args'): - val = build_info.get(key) - if val is not None: - setattr(ext, key, val) - - # Medium-easy stuff: same syntax/semantics, different names. - ext.runtime_library_dirs = build_info.get('rpath') - if 'def_file' in build_info: - log.warn("'def_file' element of build info dict " - "no longer supported") - - # Non-trivial stuff: 'macros' split into 'define_macros' - # and 'undef_macros'. - macros = build_info.get('macros') - if macros: - ext.define_macros = [] - ext.undef_macros = [] - for macro in macros: - if not (isinstance(macro, tuple) and len(macro) in (1, 2)): - raise DistutilsSetupError, \ - ("'macros' element of build info dict " - "must be 1- or 2-tuple") - if len(macro) == 1: - ext.undef_macros.append(macro[0]) - elif len(macro) == 2: - ext.define_macros.append(macro) - - extensions[i] = ext - def get_source_files(self): - self.check_extensions_list(self.extensions) filenames = [] # Wouldn't it be neat if we knew the names of header files too... @@ -485,11 +423,6 @@ return filenames def get_outputs(self): - # Sanity check the 'extensions' list -- can't assume this is being - # done in the same run as a 'build_extensions()' call (in fact, we - # can probably assume that it *isn't*!). - self.check_extensions_list(self.extensions) - # And build the list of output (built) filenames. Note that this # ignores the 'inplace' flag, and assumes everything goes in the # "build" tree. @@ -499,9 +432,6 @@ return outputs def build_extensions(self): - # First, sanity-check the 'extensions' list - self.check_extensions_list(self.extensions) - for ext in self.extensions: try: self.build_extension(ext) diff --git a/src/distutils2/command/build_py.py b/src/distutils2/command/build_py.py --- a/src/distutils2/command/build_py.py +++ b/src/distutils2/command/build_py.py @@ -6,14 +6,65 @@ import os import sys +import logging from glob import glob from distutils2.core import Command from distutils2.errors import DistutilsOptionError, DistutilsFileError from distutils2.util import convert_path -from distutils2 import log +from distutils2.converter.refactor import DistutilsRefactoringTool -class build_py(Command): +# marking public APIs +__all__ = ['Mixin2to3', 'build_py'] + +try: + from distutils2.util import Mixin2to3 as _Mixin2to3 + from lib2to3.refactor import get_fixers_from_package + _CONVERT = True + _KLASS = _Mixin2to3 +except ImportError: + _CONVERT = False + _KLASS = object + +class Mixin2to3(_KLASS): + """ The base class which can be used for refactoring. When run under + Python 3.0, the run_2to3 method provided by Mixin2to3 is overridden. + When run on Python 2.x, it merely creates a class which overrides run_2to3, + yet does nothing in particular with it. + """ + if _CONVERT: + def _run_2to3(self, files, doctests=[]): + """ Takes a list of files and doctests, and performs conversion + on those. + - First, the files which contain the code(`files`) are converted. + - Second, the doctests in `files` are converted. + - Thirdly, the doctests in `doctests` are converted. + """ + + # Convert the ".py" files. + logging.info("Converting Python code") + _KLASS.run_2to3(self, files) + + # Convert the doctests in the ".py" files. + logging.info("Converting doctests with '.py' files") + _KLASS.run_2to3(self, files, doctests_only=True) + + # If the following conditions are met, then convert:- + # 1. User has specified the 'convert_2to3_doctests' option. So, we + # can expect that the list 'doctests' is not empty. + # 2. The default is allow distutils2 to allow conversion of text files + # containing doctests. It is set as + # distutils2.run_2to3_on_doctests + + if doctests != [] and distutils2.run_2to3_on_doctests: + logging.info("Converting text files which contain doctests") + _KLASS.run_2to3(self, doctests, doctests_only=True) + else: + # If run on Python 2.x, there is nothing to do. + def _run_2to3(self, files, doctests=[]): + pass + +class build_py(Command, Mixin2to3): description = "\"build\" pure Python modules (copy to build directory)" @@ -39,6 +90,8 @@ self.compile = 0 self.optimize = 0 self.force = None + self._updated_files = [] + self._doctests_2to3 = [] def finalize_options(self): self.set_undefined_options('build', @@ -93,10 +146,18 @@ self.build_packages() self.build_package_data() + if self.distribution.use_2to3 and self_updated_files: + self.run_2to3(self._updated_files, self._doctests_2to3) + self.byte_compile(self.get_outputs(include_bytecode=0)) + # -- Top-level worker functions ------------------------------------ + def get_data_files(self): - """Generate list of '(package,src_dir,build_dir,filenames)' tuples""" + """Generate list of '(package,src_dir,build_dir,filenames)' tuples. + + Helper function for `finalize_options()`. + """ data = [] if not self.packages: return data @@ -120,7 +181,10 @@ return data def find_data_files(self, package, src_dir): - """Return filenames for package's data files in 'src_dir'""" + """Return filenames for package's data files in 'src_dir'. + + Helper function for `get_data_files()`. + """ globs = (self.package_data.get('', []) + self.package_data.get(package, [])) files = [] @@ -132,14 +196,21 @@ return files def build_package_data(self): - """Copy data files into build directory""" + """Copy data files into build directory. + + Helper function for `run()`. + """ for package, src_dir, build_dir, filenames in self.data_files: for filename in filenames: target = os.path.join(build_dir, filename) self.mkpath(os.path.dirname(target)) - self.copy_file(os.path.join(src_dir, filename), target, - preserve_mode=False) + outf, copied = self.copy_file(os.path.join(src_dir, filename), + target, preserve_mode=False) + if copied and srcfile in self.distribution.convert_2to3.doctests: + self._doctests_2to3.append(outf) + # XXX - this should be moved to the Distribution class as it is not + # only needed for build_py. It also has no dependencies on this class. def get_package_dir(self, package): """Return the directory, relative to the top of the source distribution, where package 'package' should be found @@ -181,6 +252,8 @@ return '' def check_package(self, package, package_dir): + """Helper function for `find_package_modules()` and `find_modules()'. + """ # Empty dir name means current directory, which we can probably # assume exists. Also, os.path.exists and isdir don't know about # my "empty string means current dir" convention, so we have to @@ -200,8 +273,8 @@ if os.path.isfile(init_py): return init_py else: - log.warn(("package init file '%s' not found " + - "(or not a regular file)"), init_py) + logging.warning(("package init file '%s' not found " + + "(or not a regular file)"), init_py) # Either not in a package at all (__init__.py not expected), or # __init__.py doesn't exist -- so don't return the filename. @@ -209,7 +282,8 @@ def check_module(self, module, module_file): if not os.path.isfile(module_file): - log.warn("file %s (for module %s) not found", module_file, module) + logging.warning("file %s (for module %s) not found", + module_file, module) return False else: return True diff --git a/src/distutils2/command/cmd.py b/src/distutils2/command/cmd.py --- a/src/distutils2/command/cmd.py +++ b/src/distutils2/command/cmd.py @@ -19,7 +19,7 @@ except ImportError: from distutils2._backport.shutil import make_archive -class Command: +class Command(object): """Abstract base class for defining command classes, the "worker bees" of the Distutils. A useful analogy for command classes is to think of them as subroutines with local variables called "options". The options @@ -57,8 +57,7 @@ def __init__(self, dist): """Create and initialize a new Command object. Most importantly, invokes the 'initialize_options()' method, which is the real - initializer and depends on the actual command being - instantiated. + initializer and depends on the actual command being instantiated. """ # late import because of mutual dependence between these classes from distutils2.dist import Distribution @@ -189,6 +188,31 @@ """ log.log(level, msg) + # -- External interface -------------------------------------------- + # (called by outsiders) + + def get_source_files(self): + """Return the list of files that are used as inputs to this command, + i.e. the files used to generate the output files. The result is used + by the `sdist` command in determining the set of default files. + + Command classes should implement this method if they operate on files + from the source tree. + """ + return [] + + def get_outputs(self): + """Return the list of files that would be produced if this command + were actually run. Not affected by the "dry-run" flag or whether + any other commands have been run. + + Command classes should implement this method if they produce any + output files that get consumed by another command. e.g., `build_ext` + returns the list of built extension modules, but not any temporary + files used in the compilation process. + """ + return [] + # -- Option validation methods ------------------------------------- # (these are very handy in writing the 'finalize_options()' method) # @@ -308,10 +332,8 @@ cmd_obj.ensure_finalized() return cmd_obj - # XXX rename to 'get_reinitialized_command()'? (should do the - # same in dist.py, if so) - def reinitialize_command(self, command, reinit_subcommands=0): - return self.distribution.reinitialize_command( + def get_reinitialized_command(self, command, reinit_subcommands=0): + return self.distribution.get_reinitialized_command( command, reinit_subcommands) def run_command(self, command): @@ -351,8 +373,10 @@ if os.path.isdir(name) or name == '': return if dry_run: + head = '' for part in name.split(os.sep): - self.log(part) + log.info("created directory %s%s", head, part) + head += part + os.sep return os.makedirs(name, mode) diff --git a/src/distutils2/command/register.py b/src/distutils2/command/register.py --- a/src/distutils2/command/register.py +++ b/src/distutils2/command/register.py @@ -28,8 +28,6 @@ boolean_options = PyPIRCCommand.boolean_options + [ 'verify', 'list-classifiers', 'strict'] - sub_commands = [('check', lambda self: True)] - def initialize_options(self): PyPIRCCommand.initialize_options(self) self.list_classifiers = 0 @@ -46,9 +44,8 @@ self.finalize_options() self._set_config() - # Run sub commands - for cmd_name in self.get_sub_commands(): - self.run_command(cmd_name) + # Check the package metadata + self.run_command('check') if self.dry_run: self.verify_metadata() diff --git a/src/distutils2/command/sdist.py b/src/distutils2/command/sdist.py --- a/src/distutils2/command/sdist.py +++ b/src/distutils2/command/sdist.py @@ -20,7 +20,7 @@ from distutils2.core import Command from distutils2 import util from distutils2.errors import (DistutilsPlatformError, DistutilsOptionError, - DistutilsTemplateError) + DistutilsTemplateError) from distutils2.manifest import Manifest from distutils2 import log from distutils2.util import convert_path, newer @@ -45,12 +45,6 @@ description = "create a source distribution (tarball, zip file, etc.)" - def checking_metadata(self): - """Callable used for the check sub-command. - - Placed here so user_options can view it""" - return self.metadata_check - user_options = [ ('template=', 't', "name of manifest template file [default: MANIFEST.in]"), @@ -100,8 +94,6 @@ default_format = {'posix': 'gztar', 'nt': 'zip' } - sub_commands = [('check', checking_metadata)] - def initialize_options(self): # 'template' and 'manifest' are, respectively, the names of # the manifest template and manifest file. @@ -162,9 +154,9 @@ # manifest self.filelist.clear() - # Run sub commands - for cmd_name in self.get_sub_commands(): - self.run_command(cmd_name) + # Check the package metadata + if self.metadata_check: + self.run_command('check') # Do whatever it takes to get the list of files to process # (process the manifest template, read an existing manifest, diff --git a/src/distutils2/command/upload.py b/src/distutils2/command/upload.py --- a/src/distutils2/command/upload.py +++ b/src/distutils2/command/upload.py @@ -160,7 +160,7 @@ # send the data try: result = urlopen(request) - status = result.getcode() + status = result.code reason = result.msg except socket.error, e: self.announce(str(e), log.ERROR) diff --git a/src/distutils2/command/upload_docs.py b/src/distutils2/command/upload_docs.py new file mode 100644 --- /dev/null +++ b/src/distutils2/command/upload_docs.py @@ -0,0 +1,135 @@ +import base64, httplib, os.path, socket, tempfile, urlparse, zipfile +from cStringIO import StringIO +from distutils2 import log +from distutils2.command.upload import upload +from distutils2.core import PyPIRCCommand +from distutils2.errors import DistutilsFileError + +def zip_dir(directory): + """Compresses recursively contents of directory into a StringIO object""" + destination = StringIO() + zip_file = zipfile.ZipFile(destination, "w") + for root, dirs, files in os.walk(directory): + for name in files: + full = os.path.join(root, name) + relative = root[len(directory):].lstrip(os.path.sep) + dest = os.path.join(relative, name) + zip_file.write(full, dest) + zip_file.close() + return destination + +# grabbed from +# http://code.activestate.com/recipes/146306-http-client-to-post-using-multipartform-data/ +def encode_multipart(fields, files, boundary=None): + """ + fields is a sequence of (name, value) elements for regular form fields. + files is a sequence of (name, filename, value) elements for data to be uploaded as files + Return (content_type, body) ready for httplib.HTTP instance + """ + if boundary is None: + boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254' + l = [] + for (key, value) in fields: + l.extend([ + '--' + boundary, + 'Content-Disposition: form-data; name="%s"' % key, + '', + value]) + for (key, filename, value) in files: + l.extend([ + '--' + boundary, + 'Content-Disposition: form-data; name="%s"; filename="%s"' % (key, filename), + '', + value]) + l.append('--' + boundary + '--') + l.append('') + body = '\r\n'.join(l) + content_type = 'multipart/form-data; boundary=%s' % boundary + return content_type, body + +class upload_docs(PyPIRCCommand): + + user_options = [ + ('repository=', 'r', "url of repository [default: %s]" % upload.DEFAULT_REPOSITORY), + ('show-response', None, 'display full response text from server'), + ('upload-dir=', None, 'directory to upload'), + ] + + def initialize_options(self): + PyPIRCCommand.initialize_options(self) + self.upload_dir = "build/docs" + + def finalize_options(self): + PyPIRCCommand.finalize_options(self) + if self.upload_dir == None: + build = self.get_finalized_command('build') + self.upload_dir = os.path.join(build.build_base, "docs") + self.announce('Using upload directory %s' % self.upload_dir) + self.verify_upload_dir(self.upload_dir) + config = self._read_pypirc() + if config != {}: + self.username = config['username'] + self.password = config['password'] + self.repository = config['repository'] + self.realm = config['realm'] + + def verify_upload_dir(self, upload_dir): + self.ensure_dirname('upload_dir') + index_location = os.path.join(upload_dir, "index.html") + if not os.path.exists(index_location): + mesg = "No 'index.html found in docs directory (%s)" + raise DistutilsFileError(mesg % upload_dir) + + def run(self): + tmp_dir = tempfile.mkdtemp() + name = self.distribution.metadata['Name'] + zip_file = zip_dir(self.upload_dir) + + fields = {':action': 'doc_upload', 'name': name}.items() + files = [('content', name, zip_file.getvalue())] + content_type, body = encode_multipart(fields, files) + + credentials = self.username + ':' + self.password + auth = "Basic " + base64.encodestring(credentials).strip() + + self.announce("Submitting documentation to %s" % (self.repository), + log.INFO) + + schema, netloc, url, params, query, fragments = \ + urlparse.urlparse(self.repository) + if schema == "http": + conn = httplib.HTTPConnection(netloc) + elif schema == "https": + conn = httplib.HTTPSConnection(netloc) + else: + raise AssertionError("unsupported schema "+schema) + + try: + conn.connect() + conn.putrequest("POST", url) + conn.putheader('Content-type', content_type) + conn.putheader('Content-length', str(len(body))) + conn.putheader('Authorization', auth) + conn.endheaders() + conn.send(body) + except socket.error, e: + self.announce(str(e), log.ERROR) + return + + r = conn.getresponse() + + if r.status == 200: + self.announce('Server response (%s): %s' % (r.status, r.reason), + log.INFO) + elif r.status == 301: + location = r.getheader('Location') + if location is None: + location = 'http://packages.python.org/%s/' % meta.get_name() + self.announce('Upload successful. Visit %s' % location, + log.INFO) + else: + self.announce('Upload failed (%s): %s' % (r.status, r.reason), + log.ERROR) + + if self.show_response: + print "\n".join(['-'*75, r.read(), '-'*75]) diff --git a/src/distutils2/compiler/bcppcompiler.py b/src/distutils2/compiler/bcppcompiler.py --- a/src/distutils2/compiler/bcppcompiler.py +++ b/src/distutils2/compiler/bcppcompiler.py @@ -16,7 +16,7 @@ import os from distutils2.errors import (DistutilsExecError, CompileError, LibError, - LinkError, UnknownFileError) + LinkError, UnknownFileError) from distutils2.compiler.ccompiler import CCompiler, gen_preprocess_options from distutils2.file_util import write_file from distutils2.dep_util import newer diff --git a/src/distutils2/compiler/ccompiler.py b/src/distutils2/compiler/ccompiler.py --- a/src/distutils2/compiler/ccompiler.py +++ b/src/distutils2/compiler/ccompiler.py @@ -10,7 +10,7 @@ import re from distutils2.errors import (CompileError, LinkError, UnknownFileError, - DistutilsPlatformError, DistutilsModuleError) + DistutilsPlatformError, DistutilsModuleError) from distutils2.spawn import spawn from distutils2.util import split_quoted, execute, newer_group from distutils2 import log @@ -76,7 +76,7 @@ compiler.shared_lib_extension = so_ext -class CCompiler: +class CCompiler(object): """Abstract base class to define the interface that must be implemented by real compiler classes. Also has some utility methods used by several compiler classes. @@ -800,14 +800,16 @@ library_dirs = [] fd, fname = tempfile.mkstemp(".c", funcname, text=True) f = os.fdopen(fd, "w") - for incl in includes: - f.write("""#include "%s"\n""" % incl) - f.write("""\ + try: + for incl in includes: + f.write("""#include "%s"\n""" % incl) + f.write("""\ main (int argc, char **argv) { %s(); } """ % funcname) - f.close() + finally: + f.close() try: objects = self.compile([fname], include_dirs=include_dirs) except CompileError: @@ -938,8 +940,10 @@ if os.path.isdir(name) or name == '': return if self.dry_run: + head = '' for part in name.split(os.sep): - self.log(part) + log.info("created directory %s%s", head, part) + head += part + os.sep return os.makedirs(name, mode) @@ -1048,7 +1052,7 @@ module_name = "distutils2.compiler." + module_name __import__ (module_name) module = sys.modules[module_name] - klass = vars(module)[class_name] + cls = vars(module)[class_name] except ImportError: raise DistutilsModuleError, \ "can't compile C/C++ code: unable to load module '%s'" % \ @@ -1061,7 +1065,7 @@ # XXX The None is necessary to preserve backwards compatibility # with classes that expect verbose to be the first positional # argument. - return klass(None, dry_run, force) + return cls(None, dry_run, force) def gen_preprocess_options(macros, include_dirs): diff --git a/src/distutils2/compiler/emxccompiler.py b/src/distutils2/compiler/emxccompiler.py --- a/src/distutils2/compiler/emxccompiler.py +++ b/src/distutils2/compiler/emxccompiler.py @@ -25,9 +25,8 @@ from warnings import warn from distutils2.compiler.unixccompiler import UnixCCompiler -from distutils2.util import write_file from distutils2.errors import DistutilsExecError, CompileError, UnknownFileError -from distutils2.util import get_compiler_versions +from distutils2.util import get_compiler_versions, write_file class EMXCCompiler (UnixCCompiler): @@ -273,8 +272,10 @@ # It would probably better to read single lines to search. # But we do this only once, and it is fast enough f = open(fn) - s = f.read() - f.close() + try: + s = f.read() + finally: + f.close() except IOError, exc: # if we can't read this file, we cannot say it is wrong diff --git a/src/distutils2/compiler/msvc9compiler.py b/src/distutils2/compiler/msvc9compiler.py --- a/src/distutils2/compiler/msvc9compiler.py +++ b/src/distutils2/compiler/msvc9compiler.py @@ -20,7 +20,7 @@ import re from distutils2.errors import (DistutilsExecError, DistutilsPlatformError, - CompileError, LibError, LinkError) + CompileError, LibError, LinkError) from distutils2.compiler.ccompiler import CCompiler, gen_lib_options from distutils2 import log from distutils2.util import get_platform @@ -50,7 +50,7 @@ 'win-ia64' : 'ia64', } -class Reg: +class Reg(object): """Helper class to read values from the registry """ @@ -112,7 +112,7 @@ return s convert_mbcs = staticmethod(convert_mbcs) -class MacroExpander: +class MacroExpander(object): def __init__(self, version): self.macros = {} diff --git a/src/distutils2/compiler/msvccompiler.py b/src/distutils2/compiler/msvccompiler.py --- a/src/distutils2/compiler/msvccompiler.py +++ b/src/distutils2/compiler/msvccompiler.py @@ -15,7 +15,7 @@ import string from distutils2.errors import (DistutilsExecError, DistutilsPlatformError, - CompileError, LibError, LinkError) + CompileError, LibError, LinkError) from distutils2.compiler.ccompiler import CCompiler, gen_lib_options from distutils2 import log @@ -104,7 +104,7 @@ pass return s -class MacroExpander: +class MacroExpander(object): def __init__(self, version): self.macros = {} diff --git a/src/distutils2/compiler/unixccompiler.py b/src/distutils2/compiler/unixccompiler.py --- a/src/distutils2/compiler/unixccompiler.py +++ b/src/distutils2/compiler/unixccompiler.py @@ -19,10 +19,10 @@ from types import StringType, NoneType from distutils2.util import newer -from distutils2.compiler.ccompiler import \ - CCompiler, gen_preprocess_options, gen_lib_options -from distutils2.errors import \ - DistutilsExecError, CompileError, LibError, LinkError +from distutils2.compiler.ccompiler import (CCompiler, gen_preprocess_options, + gen_lib_options) +from distutils2.errors import (DistutilsExecError, CompileError, + LibError, LinkError) from distutils2 import log try: diff --git a/src/distutils2/config.py b/src/distutils2/config.py --- a/src/distutils2/config.py +++ b/src/distutils2/config.py @@ -4,7 +4,7 @@ that uses .pypirc in the distutils.command package. """ import os -from ConfigParser import ConfigParser +from ConfigParser import RawConfigParser from distutils2.command.cmd import Command @@ -59,7 +59,7 @@ if os.path.exists(rc): self.announce('Using PyPI login from %s' % rc) repository = self.repository or self.DEFAULT_REPOSITORY - config = ConfigParser() + config = RawConfigParser() config.read(rc) sections = config.sections() if 'distutils' in sections: diff --git a/src/distutils2/converter/fixers/fix_imports.py b/src/distutils2/converter/fixers/fix_imports.py --- a/src/distutils2/converter/fixers/fix_imports.py +++ b/src/distutils2/converter/fixers/fix_imports.py @@ -36,11 +36,16 @@ pattern = [] next = imp.next_sibling while next is not None: + # Get the first child if we have a Node + if not hasattr(next, "value"): + next = next.children[0] pattern.append(next.value) if not hasattr(next, "next_sibling"): next.next_sibling = next.get_next_sibling() next = next.next_sibling - if pattern == ['import', 'setup']: + + if set(pattern).issubset(set( + ['import', ',', 'setup', 'find_packages'])): imp.value = 'distutils2.core' imp.changed() diff --git a/src/distutils2/core.py b/src/distutils2/core.py --- a/src/distutils2/core.py +++ b/src/distutils2/core.py @@ -2,8 +2,9 @@ The only module that needs to be imported to use the Distutils; provides the 'setup' function (which is to be called from the setup script). Also -indirectly provides the Distribution and Command classes, although they are -really defined in distutils2.dist and distutils2.cmd. +exports useful classes so that setup scripts can import them from here +although they are really defined in other modules: Distribution, Command, +PyPIRCommand, Extension, find_packages. """ __revision__ = "$Id: core.py 77704 2010-01-23 09:23:15Z tarek.ziade $" @@ -12,7 +13,7 @@ import os from distutils2.errors import (DistutilsSetupError, DistutilsArgError, - DistutilsError, CCompilerError) + DistutilsError, CCompilerError) from distutils2.util import grok_environment_error # Mainly import these so setup scripts can "from distutils2.core import" them. @@ -20,6 +21,7 @@ from distutils2.command.cmd import Command from distutils2.config import PyPIRCCommand from distutils2.extension import Extension +from distutils2.util import find_packages # This is a barebones help message generated displayed when the user # runs the setup script with no arguments at all. More useful help @@ -47,7 +49,8 @@ 'maintainer', 'maintainer_email', 'url', 'license', 'description', 'long_description', 'keywords', 'platforms', 'classifiers', 'download_url', - 'requires', 'provides', 'obsoletes', + 'requires', 'provides', 'obsoletes', 'use_2to3', + 'convert_2to3_doctests', ) # Legal keyword arguments for the Extension constructor @@ -94,11 +97,7 @@ # Determine the distribution class -- either caller-supplied or # our Distribution (see below). - klass = attrs.get('distclass') - if klass: - del attrs['distclass'] - else: - klass = Distribution + distclass = attrs.pop('distclass', Distribution) if 'script_name' not in attrs: attrs['script_name'] = os.path.basename(sys.argv[0]) @@ -108,7 +107,7 @@ # Create the Distribution instance, using the remaining arguments # (ie. everything except distclass) to initialize it try: - _setup_distribution = dist = klass(attrs) + _setup_distribution = dist = distclass(attrs) except DistutilsSetupError, msg: if 'name' in attrs: raise SystemExit, "error in %s setup command: %s" % \ diff --git a/src/distutils2/depgraph.py b/src/distutils2/depgraph.py --- a/src/distutils2/depgraph.py +++ b/src/distutils2/depgraph.py @@ -13,7 +13,7 @@ """ Represents a dependency graph between distributions. - The depedency relationships are stored in an ``adjacency_list`` that maps + The dependency relationships are stored in an ``adjacency_list`` that maps distributions to a list of ``(other, label)`` tuples where ``other`` is a distribution and the edge is labelled with ``label`` (i.e. the version specifier, if such was provided). Also, for more efficient traversal, for @@ -31,8 +31,7 @@ self.missing = {} def add_distribution(self, distribution): - """ - Add the *distribution* to the graph. + """Add the *distribution* to the graph. :type distribution: :class:`pkgutil.Distribution` or :class:`pkgutil.EggInfoDistribution` @@ -42,11 +41,9 @@ self.missing[distribution] = list() def add_edge(self, x, y, label=None): - """ - Add an edge from distribution *x* to distribution *y* with the given + """Add an edge from distribution *x* to distribution *y* with the given *label*. - :type x: :class:`pkgutil.Distribution` or :class:`pkgutil.EggInfoDistribution` :type y: :class:`pkgutil.Distribution` or @@ -70,8 +67,8 @@ def graph_to_dot(graph, f, skip_disconnected=True): - """ - Writes a DOT output for the graph to the provided file *f*. + """Writes a DOT output for the graph to the provided file *f*. + If *skip_disconnected* is set to ``True``, then all distributions that are not dependent on any other distribution are skipped. @@ -103,8 +100,7 @@ def generate_graph(dists): - """ - Generates a dependency graph from the given distributions. + """Generates a dependency graph from the given distributions. :parameter dists: a list of distributions :type dists: list of :class:`pkgutil.Distribution` and @@ -158,8 +154,7 @@ def dependent_dists(dists, dist): - """ - Recursively generate a list of distributions from *dists* that are + """Recursively generate a list of distributions from *dists* that are dependent on *dist*. :param dists: a list of distributions diff --git a/src/distutils2/dist.py b/src/distutils2/dist.py --- a/src/distutils2/dist.py +++ b/src/distutils2/dist.py @@ -13,8 +13,10 @@ except ImportError: warnings = None +from ConfigParser import RawConfigParser + from distutils2.errors import (DistutilsOptionError, DistutilsArgError, - DistutilsModuleError, DistutilsClassError) + DistutilsModuleError, DistutilsClassError) from distutils2.fancy_getopt import FancyGetopt, translate_longopt from distutils2.util import check_environ, strtobool from distutils2 import log @@ -110,7 +112,11 @@ ('requires', None, "print the list of packages/modules required"), ('obsoletes', None, - "print the list of packages/modules made obsolete") + "print the list of packages/modules made obsolete"), + ('use-2to3', None, + "use 2to3 to make source python 3.x compatible"), + ('convert-2to3-doctests', None, + "use 2to3 to convert doctests in seperate text files"), ] display_option_names = map(lambda x: translate_longopt(x[0]), display_options) @@ -144,8 +150,8 @@ # information here (and enough command-line options) that it's # worth it. Also delegate 'get_XXX()' methods to the 'metadata' # object in a sneaky and underhanded (but efficient!) way. + self.metadata = DistributionMetadata() - self.metadata = DistributionMetadata() #for basename in self.metadata._METHOD_BASENAMES: # method_name = "get_" + basename # setattr(self, method_name, getattr(self.metadata, method_name)) @@ -204,6 +210,8 @@ self.scripts = None self.data_files = None self.password = '' + self.use_2to3 = False + self.convert_2to3_doctests = [] # And now initialize bookkeeping stuff that can't be supplied by # the caller at all. 'command_obj' maps command names to @@ -248,7 +256,7 @@ elif hasattr(self, key): setattr(self, key, val) else: - msg = "Unknown distribution option: %s" % repr(key) + msg = "Unknown distribution option: %r" % key if warnings is not None: warnings.warn(msg) else: @@ -362,14 +370,12 @@ return files def parse_config_files(self, filenames=None): - from ConfigParser import ConfigParser - if filenames is None: filenames = self.find_config_files() log.debug("Distribution.parse_config_files():") - parser = ConfigParser() + parser = RawConfigParser() for filename in filenames: log.debug(" reading %s" % filename) parser.read(filename) @@ -383,7 +389,7 @@ opt = opt.replace('-', '_') opt_dict[opt] = (filename, val) - # Make the ConfigParser forget everything (so we retain + # Make the RawConfigParser forget everything (so we retain # the original filenames that options come from) parser.__init__() @@ -581,15 +587,11 @@ instance, analogous to the .finalize_options() method of Command objects. """ - - # XXX conversion -- removed - #for attr in ('keywords', 'platforms'): - # value = self.metadata.get_field(attr) - # if value is None: - # continue - # if isinstance(value, str): - # value = [elm.strip() for elm in value.split(',')] - # setattr(self.metadata, attr, value) + if getattr(self, 'convert_2to3_doctests', None): + self.convert_2to3_doctests = [os.path.join(p) + for p in self.convert_2to3_doctests] + else: + self.convert_2to3_doctests = [] def _show_help(self, parser, global_options=1, display_options=1, commands=[]): @@ -627,16 +629,16 @@ for command in self.commands: if isinstance(command, type) and issubclass(command, Command): - klass = command + cls = command else: - klass = self.get_command_class(command) - if (hasattr(klass, 'help_options') and - isinstance(klass.help_options, list)): - parser.set_option_table(klass.user_options + - fix_help_options(klass.help_options)) + cls = self.get_command_class(command) + if (hasattr(cls, 'help_options') and + isinstance(cls.help_options, list)): + parser.set_option_table(cls.user_options + + fix_help_options(cls.help_options)) else: - parser.set_option_table(klass.user_options) - parser.print_help("Options for '%s' command:" % klass.__name__) + parser.set_option_table(cls.user_options) + parser.print_help("Options for '%s' command:" % cls.__name__) print('') print(gen_usage(self.script_name)) @@ -688,11 +690,11 @@ print(header + ":") for cmd in commands: - klass = self.cmdclass.get(cmd) - if not klass: - klass = self.get_command_class(cmd) + cls = self.cmdclass.get(cmd) + if not cls: + cls = self.get_command_class(cmd) try: - description = klass.description + description = cls.description except AttributeError: description = "(no description available)" @@ -754,11 +756,11 @@ rv = [] for cmd in (std_commands + extra_commands): - klass = self.cmdclass.get(cmd) - if not klass: - klass = self.get_command_class(cmd) + cls = self.cmdclass.get(cmd) + if not cls: + cls = self.get_command_class(cmd) try: - description = klass.description + description = cls.description except AttributeError: description = "(no description available)" rv.append((cmd, description)) @@ -790,13 +792,13 @@ Raises DistutilsModuleError if the expected module could not be found, or if that module does not define the expected class. """ - klass = self.cmdclass.get(command) - if klass: - return klass + cls = self.cmdclass.get(command) + if cls: + return cls for pkgname in self.get_command_packages(): module_name = "%s.%s" % (pkgname, command) - klass_name = command + class_name = command try: __import__ (module_name) @@ -805,14 +807,14 @@ continue try: - klass = getattr(module, klass_name) + cls = getattr(module, class_name) except AttributeError: raise DistutilsModuleError, \ "invalid command '%s' (no class '%s' in module '%s')" \ - % (command, klass_name, module_name) + % (command, class_name, module_name) - self.cmdclass[command] = klass - return klass + self.cmdclass[command] = cls + return cls raise DistutilsModuleError("invalid command '%s'" % command) @@ -828,8 +830,8 @@ log.debug("Distribution.get_command_obj(): " \ "creating '%s' command object" % command) - klass = self.get_command_class(command) - cmd_obj = self.command_obj[command] = klass(self) + cls = self.get_command_class(command) + cmd_obj = self.command_obj[command] = cls(self) self.have_run[command] = 0 # Set any options that were supplied in config files @@ -885,7 +887,7 @@ except ValueError, msg: raise DistutilsOptionError, msg - def reinitialize_command(self, command, reinit_subcommands=0): + def get_reinitialized_command(self, command, reinit_subcommands=0): """Reinitializes a command to the state it was in when first returned by 'get_command_obj()': ie., initialized but not yet finalized. This provides the opportunity to sneak option @@ -920,7 +922,7 @@ if reinit_subcommands: for sub in command.get_sub_commands(): - self.reinitialize_command(sub, reinit_subcommands) + self.get_reinitialized_command(sub, reinit_subcommands) return command diff --git a/src/distutils2/extension.py b/src/distutils2/extension.py --- a/src/distutils2/extension.py +++ b/src/distutils2/extension.py @@ -23,7 +23,7 @@ # import that large-ish module (indirectly, through distutils.core) in # order to do anything. -class Extension: +class Extension(object): """Just a collection of attributes that describes an extension module and everything needed to build it (hopefully in a portable way, but there are hooks that let you be as unportable as you need). diff --git a/src/distutils2/fancy_getopt.py b/src/distutils2/fancy_getopt.py --- a/src/distutils2/fancy_getopt.py +++ b/src/distutils2/fancy_getopt.py @@ -30,7 +30,7 @@ # (for use as attributes of some object). longopt_xlate = string.maketrans('-', '_') -class FancyGetopt: +class FancyGetopt(object): """Wrapper around the standard 'getopt()' module that provides some handy extra functionality: * short and long options are tied together @@ -473,7 +473,7 @@ return string.translate(opt, longopt_xlate) -class OptionDummy: +class OptionDummy(object): """Dummy class just used as a place to hold command-line option values as instance attributes.""" diff --git a/src/distutils2/log.py b/src/distutils2/log.py --- a/src/distutils2/log.py +++ b/src/distutils2/log.py @@ -11,14 +11,14 @@ import sys -class Log: +class Log(object): def __init__(self, threshold=WARN): self.threshold = threshold def _log(self, level, msg, args): if level not in (DEBUG, INFO, WARN, ERROR, FATAL): - raise ValueError('%s wrong log level' % str(level)) + raise ValueError('%s wrong log level' % level) if level >= self.threshold: if args: diff --git a/src/distutils2/metadata.py b/src/distutils2/metadata.py --- a/src/distutils2/metadata.py +++ b/src/distutils2/metadata.py @@ -105,7 +105,6 @@ keys = fields.keys() possible_versions = ['1.0', '1.1', '1.2'] - # first let's try to see if a field is not part of one of the version for key in keys: if key not in _241_FIELDS and '1.0' in possible_versions: @@ -128,9 +127,9 @@ raise MetadataConflictError('You used incompatible 1.1 and 1.2 fields') # we have the choice, either 1.0, or 1.2 - # - 1.0 has a broken Summary field but work with all tools + # - 1.0 has a broken Summary field but works with all tools # - 1.1 is to avoid - # - 1.2 fixes Summary but is not spreaded yet + # - 1.2 fixes Summary but is not widespread yet if not is_1_1 and not is_1_2: # we couldn't find any specific marker if PKG_INFO_PREFERRED_VERSION in possible_versions: @@ -185,12 +184,12 @@ class DistributionMetadata(object): """Distribution meta-data class (1.0 or 1.2). """ - def __init__(self, path=None, platform_dependant=False, + def __init__(self, path=None, platform_dependent=False, execution_context=None, fileobj=None): self._fields = {} self.version = None self.docutils_support = _HAS_DOCUTILS - self.platform_dependant = platform_dependant + self.platform_dependent = platform_dependent if path is not None: self.read(path) elif fileobj is not None: @@ -263,7 +262,7 @@ return reporter.messages def _platform(self, value): - if not self.platform_dependant or ';' not in value: + if not self.platform_dependent or ';' not in value: return True, value value, marker = value.split(';') return _interpret(marker, self.execution_context), value @@ -518,7 +517,7 @@ raise NameError(value) def _nonsense_op(self): - msg = 'This operation is not supported : "%s"' % str(self) + msg = 'This operation is not supported : "%s"' % self raise SyntaxError(msg) def __call__(self): @@ -635,4 +634,3 @@ operations = _CHAIN(execution_context) tokenize(StringIO(marker).readline, operations.eat) return operations.result() - diff --git a/src/distutils2/mkpkg.py b/src/distutils2/mkpkg.py --- a/src/distutils2/mkpkg.py +++ b/src/distutils2/mkpkg.py @@ -675,7 +675,7 @@ troveDict = buildTroveDict(troveList) -class SetupClass: +class SetupClass(object): def __init__(self): self.config = None self.classifierDict = {} @@ -717,14 +717,16 @@ def inspectFile(self, path): fp = open(path, 'r') - for line in [ fp.readline() for x in range(10) ]: - m = re.match(r'^#!.*python((?P\d)(\.\d+)?)?$', line) - if m: - if m.group('major') == '3': - self.classifierDict['Programming Language :: Python :: 3'] = 1 - else: - self.classifierDict['Programming Language :: Python :: 2'] = 1 - fp.close() + try: + for line in [ fp.readline() for x in range(10) ]: + m = re.match(r'^#!.*python((?P\d)(\.\d+)?)?$', line) + if m: + if m.group('major') == '3': + self.classifierDict['Programming Language :: Python :: 3'] = 1 + else: + self.classifierDict['Programming Language :: Python :: 2'] = 1 + finally: + fp.close() def inspectDirectory(self): @@ -885,38 +887,33 @@ if os.path.exists('setup.py'): shutil.move('setup.py', 'setup.py.old') fp = open('setup.py', 'w') - fp.write('#!/usr/bin/env python\n\n') - fp.write('from distutils2.core import setup\n\n') + try: + fp.write('#!/usr/bin/env python\n\n') + fp.write('from distutils2.core import setup\n\n') + fp.write('setup(name=%s,\n' % repr(self.setupData['name'])) + fp.write(' version=%s,\n' % repr(self.setupData['version'])) + fp.write(' description=%s,\n' + % repr(self.setupData['description'])) + fp.write(' author=%s,\n' % repr(self.setupData['author'])) + fp.write(' author_email=%s,\n' + % repr(self.setupData['author_email'])) + if self.setupData['url']: + fp.write(' url=%s,\n' % repr(self.setupData['url'])) + if self.setupData['classifier']: + fp.write(' classifier=[\n') + for classifier in sorted(self.setupData['classifier'].keys()): + fp.write(' %s,\n' % repr(classifier)) + fp.write(' ],\n') + if self.setupData['packages']: + fp.write(' packages=%s,\n' + % repr(self._dotted_packages(self.setupData['packages']))) + fp.write(' package_dir=%s,\n' + % repr(self.setupData['packages'])) + fp.write(' #scripts=[\'path/to/script\']\n') - fp.write('from sys import version\n') - fp.write('if version < \'2.2.3\':\n') - fp.write(' from distutils2.dist import DistributionMetadata\n') - fp.write(' DistributionMetadata.classifier = None\n') - fp.write(' DistributionMetadata.download_url = None\n') - - fp.write('setup(name = %s,\n' % repr(self.setupData['name'])) - fp.write(' version = %s,\n' % repr(self.setupData['version'])) - fp.write(' description = %s,\n' - % repr(self.setupData['description'])) - fp.write(' author = %s,\n' % repr(self.setupData['author'])) - fp.write(' author_email = %s,\n' - % repr(self.setupData['author_email'])) - if self.setupData['url']: - fp.write(' url = %s,\n' % repr(self.setupData['url'])) - if self.setupData['classifier']: - fp.write(' classifier = [\n') - for classifier in sorted(self.setupData['classifier'].keys()): - fp.write(' %s,\n' % repr(classifier)) - fp.write(' ],\n') - if self.setupData['packages']: - fp.write(' packages = %s,\n' - % repr(self._dotted_packages(self.setupData['packages']))) - fp.write(' package_dir = %s,\n' - % repr(self.setupData['packages'])) - fp.write(' #scripts = [\'path/to/script\']\n') - - fp.write(' )\n') - fp.close() + fp.write(' )\n') + finally: + fp.close() os.chmod('setup.py', 0755) print 'Wrote "setup.py".' diff --git a/src/distutils2/pypi/__init__.py b/src/distutils2/pypi/__init__.py new file mode 100644 --- /dev/null +++ b/src/distutils2/pypi/__init__.py @@ -0,0 +1,8 @@ +"""distutils2.pypi + +Package containing ways to interact with the PyPI APIs. +""" + +__all__ = ['simple', + 'dist', +] diff --git a/src/distutils2/pypi/dist.py b/src/distutils2/pypi/dist.py new file mode 100644 --- /dev/null +++ b/src/distutils2/pypi/dist.py @@ -0,0 +1,315 @@ +"""distutils2.pypi.dist + +Provides the PyPIDistribution class thats represents a distribution retrieved +on PyPI. +""" +import re +import urlparse +import urllib +import tempfile +from operator import attrgetter + +try: + import hashlib +except ImportError: + from distutils2._backport import hashlib + +from distutils2.version import suggest_normalized_version, NormalizedVersion +from distutils2.pypi.errors import HashDoesNotMatch, UnsupportedHashName + +EXTENSIONS = ".tar.gz .tar.bz2 .tar .zip .tgz .egg".split() +MD5_HASH = re.compile(r'^.*#md5=([a-f0-9]+)$') + + +class PyPIDistribution(object): + """Represents a distribution retrieved from PyPI. + + This is a simple container for various attributes as name, version, + downloaded_location, url etc. + + The PyPIDistribution class is used by the pypi.*Index class to return + information about distributions. + """ + + @classmethod + def from_url(cls, url, probable_dist_name=None, is_external=True): + """Build a Distribution from a url archive (egg or zip or tgz). + + :param url: complete url of the distribution + :param probable_dist_name: A probable name of the distribution. + :param is_external: Tell if the url commes from an index or from + an external URL. + """ + # if the url contains a md5 hash, get it. + md5_hash = None + match = MD5_HASH.match(url) + if match is not None: + md5_hash = match.group(1) + # remove the hash + url = url.replace("#md5=%s" % md5_hash, "") + + # parse the archive name to find dist name and version + archive_name = urlparse.urlparse(url)[2].split('/')[-1] + extension_matched = False + # remove the extension from the name + for ext in EXTENSIONS: + if archive_name.endswith(ext): + archive_name = archive_name[:-len(ext)] + extension_matched = True + + name, version = split_archive_name(archive_name) + if extension_matched is True: + return PyPIDistribution(name, version, url=url, url_hashname="md5", + url_hashval=md5_hash, + url_is_external=is_external) + + def __init__(self, name, version, type=None, url=None, url_hashname=None, + url_hashval=None, url_is_external=True): + """Create a new instance of PyPIDistribution. + + :param name: the name of the distribution + :param version: the version of the distribution + :param type: the type of the dist (eg. source, bin-*, etc.) + :param url: URL where we found this distribution + :param url_hashname: the name of the hash we want to use. Refer to the + hashlib.new documentation for more information. + :param url_hashval: the hash value. + :param url_is_external: we need to know if the provided url comes from an + index browsing, or from an external resource. + + """ + self.name = name + self.version = NormalizedVersion(version) + self.type = type + # set the downloaded path to None by default. The goal here + # is to not download distributions multiple times + self.downloaded_location = None + # We store urls in dict, because we need to have a bit more informations + # than the simple URL. It will be used later to find the good url to + # use. + # We have two _url* attributes: _url and _urls. _urls contains a list of + # dict for the different urls, and _url contains the choosen url, in + # order to dont make the selection process multiple times. + self._urls = [] + self._url = None + self.add_url(url, url_hashname, url_hashval, url_is_external) + + def add_url(self, url, hashname=None, hashval=None, is_external=True): + """Add a new url to the list of urls""" + if hashname is not None: + try: + hashlib.new(hashname) + except ValueError: + raise UnsupportedHashName(hashname) + + self._urls.append({ + 'url': url, + 'hashname': hashname, + 'hashval': hashval, + 'is_external': is_external, + }) + # reset the url selection process + self._url = None + + @property + def url(self): + """Pick up the right url for the list of urls in self.urls""" + # We return internal urls over externals. + # If there is more than one internal or external, return the first + # one. + if self._url is None: + if len(self._urls) > 1: + internals_urls = [u for u in self._urls \ + if u['is_external'] == False] + if len(internals_urls) >= 1: + self._url = internals_urls[0] + if self._url is None: + self._url = self._urls[0] + return self._url + + @property + def is_source(self): + """return if the distribution is a source one or not""" + return self.type == 'source' + + @property + def is_final(self): + """proxy to version.is_final""" + return self.version.is_final + + def download(self, path=None): + """Download the distribution to a path, and return it. + + If the path is given in path, use this, otherwise, generates a new one + """ + if path is None: + path = tempfile.mkdtemp() + + # if we do not have downloaded it yet, do it. + if self.downloaded_location is None: + url = self.url['url'] + archive_name = urlparse.urlparse(url)[2].split('/')[-1] + filename, headers = urllib.urlretrieve(url, + path + "/" + archive_name) + self.downloaded_location = filename + self._check_md5(filename) + return self.downloaded_location + + def _check_md5(self, filename): + """Check that the md5 checksum of the given file matches the one in + url param""" + hashname = self.url['hashname'] + expected_hashval = self.url['hashval'] + if not None in (expected_hashval, hashname): + f = open(filename) + hashval = hashlib.new(hashname) + hashval.update(f.read()) + if hashval.hexdigest() != expected_hashval: + raise HashDoesNotMatch("got %s instead of %s" + % (hashval.hexdigest(), expected_hashval)) + + def __repr__(self): + return "%s %s %s %s" \ + % (self.__class__.__name__, self.name, self.version, + self.type or "") + + def _check_is_comparable(self, other): + if not isinstance(other, PyPIDistribution): + raise TypeError("cannot compare %s and %s" + % (type(self).__name__, type(other).__name__)) + elif self.name != other.name: + raise TypeError("cannot compare %s and %s" + % (self.name, other.name)) + + def __eq__(self, other): + self._check_is_comparable(other) + return self.version == other.version + + def __lt__(self, other): + self._check_is_comparable(other) + return self.version < other.version + + def __ne__(self, other): + return not self.__eq__(other) + + def __gt__(self, other): + return not (self.__lt__(other) or self.__eq__(other)) + + def __le__(self, other): + return self.__eq__(other) or self.__lt__(other) + + def __ge__(self, other): + return self.__eq__(other) or self.__gt__(other) + + # See http://docs.python.org/reference/datamodel#object.__hash__ + __hash__ = object.__hash__ + + +class PyPIDistributions(list): + """A container of PyPIDistribution objects. + + Contains methods and facilities to sort and filter distributions. + """ + def __init__(self, list=[]): + # To disable the ability to pass lists on instanciation + super(PyPIDistributions, self).__init__() + for item in list: + self.append(item) + + def filter(self, predicate): + """Filter the distributions and return a subset of distributions that + match the given predicate + """ + return PyPIDistributions( + [dist for dist in self if dist.name == predicate.name and + predicate.match(dist.version)]) + + def get_last(self, predicate, prefer_source=None, prefer_final=None): + """Return the most up to date version, that satisfy the given + predicate + """ + distributions = self.filter(predicate) + distributions.sort_distributions(prefer_source, prefer_final, reverse=True) + return distributions[0] + + def get_same_name_and_version(self): + """Return lists of PyPIDistribution objects that refer to the same + name and version number. This do not consider the type (source, binary, + etc.)""" + processed = [] + duplicates = [] + for dist in self: + if (dist.name, dist.version) not in processed: + processed.append((dist.name, dist.version)) + found_duplicates = [d for d in self if d.name == dist.name and + d.version == dist.version] + if len(found_duplicates) > 1: + duplicates.append(found_duplicates) + return duplicates + + def append(self, o): + """Append a new distribution to the list. + + If a distribution with the same name and version exists, just grab the + URL informations and add a new new url for the existing one. + """ + similar_dists = [d for d in self if d.name == o.name and + d.version == o.version and d.type == o.type] + if len(similar_dists) > 0: + dist = similar_dists[0] + dist.add_url(**o.url) + else: + super(PyPIDistributions, self).append(o) + + def sort_distributions(self, prefer_source=True, prefer_final=False, + reverse=True, *args, **kwargs): + """order the results with the given properties""" + + sort_by = [] + if prefer_final: + sort_by.append("is_final") + sort_by.append("version") + + if prefer_source: + sort_by.append("is_source") + + super(PyPIDistributions, self).sort( + key=lambda i: [getattr(i, arg) for arg in sort_by], + reverse=reverse, *args, **kwargs) + + +def split_archive_name(archive_name, probable_name=None): + """Split an archive name into two parts: name and version. + + Return the tuple (name, version) + """ + # Try to determine wich part is the name and wich is the version using the + # "-" separator. Take the larger part to be the version number then reduce + # if this not works. + def eager_split(str, maxsplit=2): + # split using the "-" separator + splits = str.rsplit("-", maxsplit) + name = splits[0] + version = "-".join(splits[1:]) + if version.startswith("-"): + version = version[1:] + if suggest_normalized_version(version) is None and maxsplit >= 0: + # we dont get a good version number: recurse ! + return eager_split(str, maxsplit - 1) + else: + return (name, version) + if probable_name is not None: + probable_name = probable_name.lower() + name = None + if probable_name is not None and probable_name in archive_name: + # we get the name from probable_name, if given. + name = probable_name + version = archive_name.lstrip(name) + else: + name, version = eager_split(archive_name) + + version = suggest_normalized_version(version) + if version != "" and name != "": + return (name.lower(), version) + else: + raise CantParseArchiveName(archive_name) diff --git a/src/distutils2/pypi/errors.py b/src/distutils2/pypi/errors.py new file mode 100644 --- /dev/null +++ b/src/distutils2/pypi/errors.py @@ -0,0 +1,33 @@ +"""distutils2.pypi.errors + +All errors and exceptions raised by PyPiIndex classes. +""" +from distutils2.errors import DistutilsError + + +class PyPIError(DistutilsError): + """The base class for errors of the pypi python package.""" + + +class DistributionNotFound(PyPIError): + """No distribution match the given requirements.""" + + +class CantParseArchiveName(PyPIError): + """An archive name can't be parsed to find distribution name and version""" + + +class DownloadError(PyPIError): + """An error has occurs while downloading""" + + +class HashDoesNotMatch(DownloadError): + """Compared hashes does not match""" + + +class UnsupportedHashName(PyPIError): + """A unsupported hashname has been used""" + + +class UnableToDownload(PyPIError): + """All mirrors have been tried, without success""" diff --git a/src/distutils2/pypi/simple.py b/src/distutils2/pypi/simple.py new file mode 100644 --- /dev/null +++ b/src/distutils2/pypi/simple.py @@ -0,0 +1,393 @@ +"""pypi.simple + +Contains the class "SimpleIndex", a simple spider to find and retrieve +distributions on the Python Package Index, using it's "simple" API, +avalaible at http://pypi.python.org/simple/ +""" +from fnmatch import translate +import httplib +import re +import socket +import sys +import urllib2 +import urlparse + +from distutils2.version import VersionPredicate +from distutils2.pypi.dist import (PyPIDistribution, PyPIDistributions, + EXTENSIONS) +from distutils2.pypi.errors import (PyPIError, DistributionNotFound, + DownloadError, UnableToDownload) +from distutils2 import __version__ as __distutils2_version__ + +# -- Constants ----------------------------------------------- +PYPI_DEFAULT_INDEX_URL = "http://pypi.python.org/simple/" +PYPI_DEFAULT_MIRROR_URL = "mirrors.pypi.python.org" +DEFAULT_HOSTS = ("*",) +SOCKET_TIMEOUT = 15 +USER_AGENT = "Python-urllib/%s distutils2/%s" % ( + sys.version[:3], __distutils2_version__) + +# -- Regexps ------------------------------------------------- +EGG_FRAGMENT = re.compile(r'^egg=([-A-Za-z0-9_.]+)$') +HREF = re.compile("""href\\s*=\\s*['"]?([^'"> ]+)""", re.I) +PYPI_MD5 = re.compile( + '([^<]+)\n\s+\\(md5\\)') +URL_SCHEME = re.compile('([-+.a-z0-9]{2,}):', re.I).match + +# This pattern matches a character entity reference (a decimal numeric +# references, a hexadecimal numeric reference, or a named reference). +ENTITY_SUB = re.compile(r'&(#(\d+|x[\da-fA-F]+)|[\w.:-]+);?').sub +REL = re.compile("""<([^>]*\srel\s*=\s*['"]?([^'">]+)[^>]*)>""", re.I) + + +def socket_timeout(timeout=SOCKET_TIMEOUT): + """Decorator to add a socket timeout when requesting pages on PyPI. + """ + def _socket_timeout(func): + def _socket_timeout(self, *args, **kwargs): + old_timeout = socket.getdefaulttimeout() + if hasattr(self, "_timeout"): + timeout = self._timeout + socket.setdefaulttimeout(timeout) + try: + return func(self, *args, **kwargs) + finally: + socket.setdefaulttimeout(old_timeout) + return _socket_timeout + return _socket_timeout + + +class SimpleIndex(object): + """Provides useful tools to request the Python Package Index simple API + + :param index_url: the url of the simple index to search on. + :param follow_externals: tell if following external links is needed or + not. Default is False. + :param hosts: a list of hosts allowed to be processed while using + follow_externals=True. Default behavior is to follow all + hosts. + :param follow_externals: tell if following external links is needed or + not. Default is False. + :param prefer_source: if there is binary and source distributions, the + source prevails. + :param prefer_final: if the version is not mentioned, and the last + version is not a "final" one (alpha, beta, etc.), + pick up the last final version. + :param mirrors_url: the url to look on for DNS records giving mirror + adresses. + :param mirrors: a list of mirrors to check out if problems + occurs while working with the one given in "url" + :param timeout: time in seconds to consider a url has timeouted. + """ + + def __init__(self, index_url=PYPI_DEFAULT_INDEX_URL, hosts=DEFAULT_HOSTS, + follow_externals=False, prefer_source=True, + prefer_final=False, mirrors_url=PYPI_DEFAULT_MIRROR_URL, + mirrors=None, timeout=SOCKET_TIMEOUT): + self.follow_externals = follow_externals + + if not index_url.endswith("/"): + index_url += "/" + self._index_urls = [index_url] + # if no mirrors are defined, use the method described in PEP 381. + if mirrors is None: + try: + mirrors = socket.gethostbyname_ex(mirrors_url)[-1] + except socket.gaierror: + mirrors = [] + self._index_urls.extend(mirrors) + self._current_index_url = 0 + self._timeout = timeout + self._prefer_source = prefer_source + self._prefer_final = prefer_final + + # create a regexp to match all given hosts + self._allowed_hosts = re.compile('|'.join(map(translate, hosts))).match + + # we keep an index of pages we have processed, in order to avoid + # scanning them multple time (eg. if there is multiple pages pointing + # on one) + self._processed_urls = [] + self._distributions = {} + + def find(self, requirements, prefer_source=None, prefer_final=None): + """Browse the PyPI to find distributions that fullfil the given + requirements. + + :param requirements: A project name and it's distribution, using + version specifiers, as described in PEP345. + :type requirements: You can pass either a version.VersionPredicate + or a string. + :param prefer_source: if there is binary and source distributions, the + source prevails. + :param prefer_final: if the version is not mentioned, and the last + version is not a "final" one (alpha, beta, etc.), + pick up the last final version. + """ + requirements = self._get_version_predicate(requirements) + if prefer_source is None: + prefer_source = self._prefer_source + if prefer_final is None: + prefer_final = self._prefer_final + + # process the index for this project + self._process_pypi_page(requirements.name) + + # filter with requirements and return the results + if requirements.name in self._distributions: + dists = self._distributions[requirements.name].filter(requirements) + dists.sort_distributions(prefer_source=prefer_source, + prefer_final=prefer_final) + else: + dists = [] + + return dists + + def get(self, requirements, *args, **kwargs): + """Browse the PyPI index to find distributions that fullfil the + given requirements, and return the most recent one. + + You can specify prefer_final and prefer_source arguments here. + If not, the default one will be used. + """ + predicate = self._get_version_predicate(requirements) + dists = self.find(predicate, *args, **kwargs) + + if len(dists) == 0: + raise DistributionNotFound(requirements) + + return dists.get_last(predicate) + + def download(self, requirements, temp_path=None, *args, **kwargs): + """Download the distribution, using the requirements. + + If more than one distribution match the requirements, use the last + version. + Download the distribution, and put it in the temp_path. If no temp_path + is given, creates and return one. + + Returns the complete absolute path to the downloaded archive. + + :param requirements: The same as the find attribute of `find`. + + You can specify prefer_final and prefer_source arguments here. + If not, the default one will be used. + """ + return self.get(requirements, *args, **kwargs)\ + .download(path=temp_path) + + def _get_version_predicate(self, requirements): + """Return a VersionPredicate object, from a string or an already + existing object. + """ + if isinstance(requirements, str): + requirements = VersionPredicate(requirements) + return requirements + + @property + def index_url(self): + return self._index_urls[self._current_index_url] + + def _switch_to_next_mirror(self): + """Switch to the next mirror (eg. point self.index_url to the next + url. + """ + # Internally, iter over the _index_url iterable, if we have read all + # of the available indexes, raise an exception. + if self._current_index_url < len(self._index_urls): + self._current_index_url = self._current_index_url + 1 + else: + raise UnableToDownload("All mirrors fails") + + def _is_browsable(self, url): + """Tell if the given URL can be browsed or not. + + It uses the follow_externals and the hosts list to tell if the given + url is browsable or not. + """ + # if _index_url is contained in the given URL, we are browsing the + # index, and it's always "browsable". + # local files are always considered browable resources + if self.index_url in url or urlparse.urlparse(url)[0] == "file": + return True + elif self.follow_externals: + if self._allowed_hosts(urlparse.urlparse(url)[1]): # 1 is netloc + return True + else: + return False + return False + + def _is_distribution(self, link): + """Tell if the given URL matches to a distribution name or not. + """ + #XXX find a better way to check that links are distributions + # Using a regexp ? + for ext in EXTENSIONS: + if ext in link: + return True + return False + + def _register_dist(self, dist): + """Register a distribution as a part of fetched distributions for + SimpleIndex. + + Return the PyPIDistributions object for the specified project name + """ + # Internally, check if a entry exists with the project name, if not, + # create a new one, and if exists, add the dist to the pool. + if not dist.name in self._distributions: + self._distributions[dist.name] = PyPIDistributions() + self._distributions[dist.name].append(dist) + return self._distributions[dist.name] + + def _process_url(self, url, project_name=None, follow_links=True): + """Process an url and search for distributions packages. + + For each URL found, if it's a download, creates a PyPIdistribution + object. If it's a homepage and we can follow links, process it too. + + :param url: the url to process + :param project_name: the project name we are searching for. + :param follow_links: Do not want to follow links more than from one + level. This parameter tells if we want to follow + the links we find (eg. run recursively this + method on it) + """ + f = self._open_url(url) + base_url = f.url + if url not in self._processed_urls: + self._processed_urls.append(url) + link_matcher = self._get_link_matcher(url) + for link, is_download in link_matcher(f.read(), base_url): + if link not in self._processed_urls: + if self._is_distribution(link) or is_download: + self._processed_urls.append(link) + # it's a distribution, so create a dist object + dist = PyPIDistribution.from_url(link, project_name, + is_external=not self.index_url in url) + self._register_dist(dist) + else: + if self._is_browsable(link) and follow_links: + self._process_url(link, project_name, + follow_links=False) + + def _get_link_matcher(self, url): + """Returns the right link matcher function of the given url + """ + if self.index_url in url: + return self._simple_link_matcher + else: + return self._default_link_matcher + + def _simple_link_matcher(self, content, base_url): + """Yield all links with a rel="download" or rel="homepage". + + This matches the simple index requirements for matching links. + If follow_externals is set to False, dont yeld the external + urls. + """ + for match in REL.finditer(content): + tag, rel = match.groups() + rels = map(str.strip, rel.lower().split(',')) + if 'homepage' in rels or 'download' in rels: + for match in HREF.finditer(tag): + url = urlparse.urljoin(base_url, + self._htmldecode(match.group(1))) + if 'download' in rels or self._is_browsable(url): + # yield a list of (url, is_download) + yield (urlparse.urljoin(base_url, url), + 'download' in rels) + + def _default_link_matcher(self, content, base_url): + """Yield all links found on the page. + """ + for match in HREF.finditer(content): + url = urlparse.urljoin(base_url, self._htmldecode(match.group(1))) + if self._is_browsable(url): + yield (url, False) + + def _process_pypi_page(self, name): + """Find and process a PyPI page for the given project name. + + :param name: the name of the project to find the page + """ + try: + # Browse and index the content of the given PyPI page. + url = self.index_url + name + "/" + self._process_url(url, name) + except DownloadError: + # if an error occurs, try with the next index_url + # (provided by the mirrors) + self._switch_to_next_mirror() + self._distributions.clear() + self._process_pypi_page(name) + + @socket_timeout() + def _open_url(self, url): + """Open a urllib2 request, handling HTTP authentication, and local + files support. + + """ + try: + scheme, netloc, path, params, query, frag = urlparse.urlparse(url) + + if scheme in ('http', 'https'): + auth, host = urllib2.splituser(netloc) + else: + auth = None + + # add index.html automatically for filesystem paths + if scheme == 'file': + if url.endswith('/'): + url += "index.html" + + if auth: + auth = "Basic " + \ + urllib2.unquote(auth).encode('base64').strip() + new_url = urlparse.urlunparse(( + scheme, host, path, params, query, frag)) + request = urllib2.Request(new_url) + request.add_header("Authorization", auth) + else: + request = urllib2.Request(url) + request.add_header('User-Agent', USER_AGENT) + fp = urllib2.urlopen(request) + + if auth: + # Put authentication info back into request URL if same host, + # so that links found on the page will work + s2, h2, path2, param2, query2, frag2 = \ + urlparse.urlparse(fp.url) + if s2 == scheme and h2 == host: + fp.url = urlparse.urlunparse( + (s2, netloc, path2, param2, query2, frag2)) + + return fp + except (ValueError, httplib.InvalidURL), v: + msg = ' '.join([str(arg) for arg in v.args]) + raise PyPIError('%s %s' % (url, msg)) + except urllib2.HTTPError, v: + return v + except urllib2.URLError, v: + raise DownloadError("Download error for %s: %s" % (url, v.reason)) + except httplib.BadStatusLine, v: + raise DownloadError('%s returned a bad status line. ' + 'The server might be down, %s' % (url, v.line)) + except httplib.HTTPException, v: + raise DownloadError("Download error for %s: %s" % (url, v)) + + def _decode_entity(self, match): + what = match.group(1) + if what.startswith('#x'): + what = int(what[2:], 16) + elif what.startswith('#'): + what = int(what[1:]) + else: + from htmlentitydefs import name2codepoint + what = name2codepoint.get(what, match.group(0)) + return unichr(what) + + def _htmldecode(self, text): + """Decode HTML entities in the given text.""" + return ENTITY_SUB(self._decode_entity, text) diff --git a/src/distutils2/spawn.py b/src/distutils2/spawn.py --- a/src/distutils2/spawn.py +++ b/src/distutils2/spawn.py @@ -14,7 +14,7 @@ from distutils2.errors import DistutilsPlatformError, DistutilsExecError from distutils2 import log -def spawn(cmd, search_path=1, verbose=0, dry_run=0): +def spawn(cmd, search_path=1, verbose=0, dry_run=0, env=None): """Run another program, specified as a command list 'cmd', in a new process. 'cmd' is just the argument list for the new process, ie. @@ -27,15 +27,18 @@ must be the exact path to the executable. If 'dry_run' is true, the command will not actually be run. + If 'env' is given, it's a environment dictionary used for the execution + environment. + Raise DistutilsExecError if running the program fails in any way; just return on success. """ if os.name == 'posix': - _spawn_posix(cmd, search_path, dry_run=dry_run) + _spawn_posix(cmd, search_path, dry_run=dry_run, env=env) elif os.name == 'nt': - _spawn_nt(cmd, search_path, dry_run=dry_run) + _spawn_nt(cmd, search_path, dry_run=dry_run, env=env) elif os.name == 'os2': - _spawn_os2(cmd, search_path, dry_run=dry_run) + _spawn_os2(cmd, search_path, dry_run=dry_run, env=env) else: raise DistutilsPlatformError, \ "don't know how to spawn programs on platform '%s'" % os.name @@ -56,7 +59,7 @@ args[i] = '"%s"' % arg return args -def _spawn_nt(cmd, search_path=1, verbose=0, dry_run=0): +def _spawn_nt(cmd, search_path=1, verbose=0, dry_run=0, env=None): executable = cmd[0] cmd = _nt_quote_args(cmd) if search_path: @@ -66,7 +69,11 @@ if not dry_run: # spawn for NT requires a full path to the .exe try: - rc = os.spawnv(os.P_WAIT, executable, cmd) + if env is None: + rc = os.spawnv(os.P_WAIT, executable, cmd) + else: + rc = os.spawnve(os.P_WAIT, executable, cmd, env) + except OSError, exc: # this seems to happen when the command isn't found raise DistutilsExecError, \ @@ -76,7 +83,7 @@ raise DistutilsExecError, \ "command '%s' failed with exit status %d" % (cmd[0], rc) -def _spawn_os2(cmd, search_path=1, verbose=0, dry_run=0): +def _spawn_os2(cmd, search_path=1, verbose=0, dry_run=0, env=None): executable = cmd[0] if search_path: # either we find one or it stays the same @@ -85,7 +92,11 @@ if not dry_run: # spawnv for OS/2 EMX requires a full path to the .exe try: - rc = os.spawnv(os.P_WAIT, executable, cmd) + if env is None: + rc = os.spawnv(os.P_WAIT, executable, cmd) + else: + rc = os.spawnve(os.P_WAIT, executable, cmd, env) + except OSError, exc: # this seems to happen when the command isn't found raise DistutilsExecError, \ @@ -97,16 +108,24 @@ "command '%s' failed with exit status %d" % (cmd[0], rc) -def _spawn_posix(cmd, search_path=1, verbose=0, dry_run=0): +def _spawn_posix(cmd, search_path=1, verbose=0, dry_run=0, env=None): log.info(' '.join(cmd)) if dry_run: return - exec_fn = search_path and os.execvp or os.execv + + if env is None: + exec_fn = search_path and os.execvp or os.execv + else: + exec_fn = search_path and os.execvpe or os.execve + pid = os.fork() if pid == 0: # in the child try: - exec_fn(cmd[0], cmd) + if env is None: + exec_fn(cmd[0], cmd) + else: + exec_fn(cmd[0], cmd, env) except OSError, e: sys.stderr.write("unable to execute %s: %s\n" % (cmd[0], e.strerror)) diff --git a/src/distutils2/tests/__init__.py b/src/distutils2/tests/__init__.py --- a/src/distutils2/tests/__init__.py +++ b/src/distutils2/tests/__init__.py @@ -45,7 +45,7 @@ """Test failed.""" -class BasicTestRunner: +class BasicTestRunner(object): def run(self, test): result = unittest.TestResult() test(result) diff --git a/src/distutils2/tests/conversions/05_after.py b/src/distutils2/tests/conversions/05_after.py new file mode 100644 --- /dev/null +++ b/src/distutils2/tests/conversions/05_after.py @@ -0,0 +1,137 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright (C) 2003-2009 Edgewall Software +# All rights reserved. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at http://trac.edgewall.org/wiki/TracLicense. +# +# This software consists of voluntary contributions made by many +# individuals. For the exact contribution history, see the revision +# history and logs, available at http://trac.edgewall.org/log/. + +from distutils2.core import setup, find_packages + +extra = {} + +try: + import babel + + extractors = [ + ('**.py', 'python', None), + ('**/templates/**.html', 'genshi', None), + ('**/templates/**.txt', 'genshi', + {'template_class': 'genshi.template:NewTextTemplate'}), + ] + extra['message_extractors'] = { + 'trac': extractors, + 'tracopt': extractors, + } + + from trac.util.dist import get_l10n_js_cmdclass + extra['cmdclass'] = get_l10n_js_cmdclass() + +except ImportError, e: + pass + +setup( + name = 'Trac', + version = '0.12.1', + summary = 'Integrated SCM, wiki, issue tracker and project environment', + description = """ +Trac is a minimalistic web-based software project management and bug/issue +tracking system. It provides an interface to the Subversion revision control +systems, an integrated wiki, flexible issue tracking and convenient report +facilities. +""", + author = 'Edgewall Software', + author_email = 'info at edgewall.com', + license = 'BSD', + home_page = 'http://trac.edgewall.org/', + download_url = 'http://trac.edgewall.org/wiki/TracDownload', + classifiers = [ + 'Environment :: Web Environment', + 'Framework :: Trac', + 'Intended Audience :: Developers', + 'License :: OSI Approved :: BSD License', + 'Operating System :: OS Independent', + 'Programming Language :: Python', + 'Topic :: Software Development :: Bug Tracking', + 'Topic :: Software Development :: Version Control', + ], + + packages = find_packages(exclude=['*.tests']), + package_data = { + '': ['templates/*'], + 'trac': ['htdocs/*.*', 'htdocs/README', 'htdocs/js/*.*', + 'htdocs/js/messages/*.*', 'htdocs/css/*.*', + 'htdocs/guide/*', 'locale/*/LC_MESSAGES/messages.mo'], + 'trac.wiki': ['default-pages/*'], + 'trac.ticket': ['workflows/*.ini'], + }, + + test_suite = 'trac.test.suite', + zip_safe = True, + + requires_dist = [ + 'setuptools>=0.6b1', + 'Genshi>=0.6', + ], + extras_require = { + 'Babel': ['Babel>=0.9.5'], + 'Pygments': ['Pygments>=0.6'], + 'reST': ['docutils>=0.3'], + 'SilverCity': ['SilverCity>=0.9.4'], + 'Textile': ['textile>=2.0'], + }, + + entry_points = """ + [console_scripts] + trac-admin = trac.admin.console:run + tracd = trac.web.standalone:main + + [trac.plugins] + trac.about = trac.about + trac.admin.console = trac.admin.console + trac.admin.web_ui = trac.admin.web_ui + trac.attachment = trac.attachment + trac.db.mysql = trac.db.mysql_backend + trac.db.postgres = trac.db.postgres_backend + trac.db.sqlite = trac.db.sqlite_backend + trac.mimeview.patch = trac.mimeview.patch + trac.mimeview.pygments = trac.mimeview.pygments[Pygments] + trac.mimeview.rst = trac.mimeview.rst[reST] + trac.mimeview.silvercity = trac.mimeview.silvercity[SilverCity] + trac.mimeview.txtl = trac.mimeview.txtl[Textile] + trac.prefs = trac.prefs.web_ui + trac.search = trac.search.web_ui + trac.ticket.admin = trac.ticket.admin + trac.ticket.query = trac.ticket.query + trac.ticket.report = trac.ticket.report + trac.ticket.roadmap = trac.ticket.roadmap + trac.ticket.web_ui = trac.ticket.web_ui + trac.timeline = trac.timeline.web_ui + trac.versioncontrol.admin = trac.versioncontrol.admin + trac.versioncontrol.svn_authz = trac.versioncontrol.svn_authz + trac.versioncontrol.svn_fs = trac.versioncontrol.svn_fs + trac.versioncontrol.svn_prop = trac.versioncontrol.svn_prop + trac.versioncontrol.web_ui = trac.versioncontrol.web_ui + trac.web.auth = trac.web.auth + trac.web.session = trac.web.session + trac.wiki.admin = trac.wiki.admin + trac.wiki.interwiki = trac.wiki.interwiki + trac.wiki.macros = trac.wiki.macros + trac.wiki.web_ui = trac.wiki.web_ui + trac.wiki.web_api = trac.wiki.web_api + tracopt.mimeview.enscript = tracopt.mimeview.enscript + tracopt.mimeview.php = tracopt.mimeview.php + tracopt.perm.authz_policy = tracopt.perm.authz_policy + tracopt.perm.config_perm_provider = tracopt.perm.config_perm_provider + tracopt.ticket.commit_updater = tracopt.ticket.commit_updater + tracopt.ticket.deleter = tracopt.ticket.deleter + """, + + **extra +) diff --git a/src/distutils2/tests/conversions/05_before.py b/src/distutils2/tests/conversions/05_before.py new file mode 100755 --- /dev/null +++ b/src/distutils2/tests/conversions/05_before.py @@ -0,0 +1,137 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright (C) 2003-2009 Edgewall Software +# All rights reserved. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at http://trac.edgewall.org/wiki/TracLicense. +# +# This software consists of voluntary contributions made by many +# individuals. For the exact contribution history, see the revision +# history and logs, available at http://trac.edgewall.org/log/. + +from setuptools import setup, find_packages + +extra = {} + +try: + import babel + + extractors = [ + ('**.py', 'python', None), + ('**/templates/**.html', 'genshi', None), + ('**/templates/**.txt', 'genshi', + {'template_class': 'genshi.template:NewTextTemplate'}), + ] + extra['message_extractors'] = { + 'trac': extractors, + 'tracopt': extractors, + } + + from trac.util.dist import get_l10n_js_cmdclass + extra['cmdclass'] = get_l10n_js_cmdclass() + +except ImportError, e: + pass + +setup( + name = 'Trac', + version = '0.12.1', + description = 'Integrated SCM, wiki, issue tracker and project environment', + long_description = """ +Trac is a minimalistic web-based software project management and bug/issue +tracking system. It provides an interface to the Subversion revision control +systems, an integrated wiki, flexible issue tracking and convenient report +facilities. +""", + author = 'Edgewall Software', + author_email = 'info at edgewall.com', + license = 'BSD', + url = 'http://trac.edgewall.org/', + download_url = 'http://trac.edgewall.org/wiki/TracDownload', + classifiers = [ + 'Environment :: Web Environment', + 'Framework :: Trac', + 'Intended Audience :: Developers', + 'License :: OSI Approved :: BSD License', + 'Operating System :: OS Independent', + 'Programming Language :: Python', + 'Topic :: Software Development :: Bug Tracking', + 'Topic :: Software Development :: Version Control', + ], + + packages = find_packages(exclude=['*.tests']), + package_data = { + '': ['templates/*'], + 'trac': ['htdocs/*.*', 'htdocs/README', 'htdocs/js/*.*', + 'htdocs/js/messages/*.*', 'htdocs/css/*.*', + 'htdocs/guide/*', 'locale/*/LC_MESSAGES/messages.mo'], + 'trac.wiki': ['default-pages/*'], + 'trac.ticket': ['workflows/*.ini'], + }, + + test_suite = 'trac.test.suite', + zip_safe = True, + + install_requires = [ + 'setuptools>=0.6b1', + 'Genshi>=0.6', + ], + extras_require = { + 'Babel': ['Babel>=0.9.5'], + 'Pygments': ['Pygments>=0.6'], + 'reST': ['docutils>=0.3'], + 'SilverCity': ['SilverCity>=0.9.4'], + 'Textile': ['textile>=2.0'], + }, + + entry_points = """ + [console_scripts] + trac-admin = trac.admin.console:run + tracd = trac.web.standalone:main + + [trac.plugins] + trac.about = trac.about + trac.admin.console = trac.admin.console + trac.admin.web_ui = trac.admin.web_ui + trac.attachment = trac.attachment + trac.db.mysql = trac.db.mysql_backend + trac.db.postgres = trac.db.postgres_backend + trac.db.sqlite = trac.db.sqlite_backend + trac.mimeview.patch = trac.mimeview.patch + trac.mimeview.pygments = trac.mimeview.pygments[Pygments] + trac.mimeview.rst = trac.mimeview.rst[reST] + trac.mimeview.silvercity = trac.mimeview.silvercity[SilverCity] + trac.mimeview.txtl = trac.mimeview.txtl[Textile] + trac.prefs = trac.prefs.web_ui + trac.search = trac.search.web_ui + trac.ticket.admin = trac.ticket.admin + trac.ticket.query = trac.ticket.query + trac.ticket.report = trac.ticket.report + trac.ticket.roadmap = trac.ticket.roadmap + trac.ticket.web_ui = trac.ticket.web_ui + trac.timeline = trac.timeline.web_ui + trac.versioncontrol.admin = trac.versioncontrol.admin + trac.versioncontrol.svn_authz = trac.versioncontrol.svn_authz + trac.versioncontrol.svn_fs = trac.versioncontrol.svn_fs + trac.versioncontrol.svn_prop = trac.versioncontrol.svn_prop + trac.versioncontrol.web_ui = trac.versioncontrol.web_ui + trac.web.auth = trac.web.auth + trac.web.session = trac.web.session + trac.wiki.admin = trac.wiki.admin + trac.wiki.interwiki = trac.wiki.interwiki + trac.wiki.macros = trac.wiki.macros + trac.wiki.web_ui = trac.wiki.web_ui + trac.wiki.web_api = trac.wiki.web_api + tracopt.mimeview.enscript = tracopt.mimeview.enscript + tracopt.mimeview.php = tracopt.mimeview.php + tracopt.perm.authz_policy = tracopt.perm.authz_policy + tracopt.perm.config_perm_provider = tracopt.perm.config_perm_provider + tracopt.ticket.commit_updater = tracopt.ticket.commit_updater + tracopt.ticket.deleter = tracopt.ticket.deleter + """, + + **extra +) diff --git a/src/distutils2/tests/pypi_server.py b/src/distutils2/tests/pypi_server.py new file mode 100644 --- /dev/null +++ b/src/distutils2/tests/pypi_server.py @@ -0,0 +1,195 @@ +"""Mocked PyPI Server implementation, to use in tests. + +This module also provides a simple test case to extend if you need to use +the PyPIServer all along your test case. Be sure to read the documentation +before any use. +""" + +import Queue +import threading +import time +import urllib2 +from BaseHTTPServer import HTTPServer +from SimpleHTTPServer import SimpleHTTPRequestHandler +import os.path +import select + +from distutils2.tests.support import unittest + +PYPI_DEFAULT_STATIC_PATH = os.path.dirname(os.path.abspath(__file__)) + "/pypiserver" + +def use_pypi_server(*server_args, **server_kwargs): + """Decorator to make use of the PyPIServer for test methods, + just when needed, and not for the entire duration of the testcase. + """ + def wrapper(func): + def wrapped(*args, **kwargs): + server = PyPIServer(*server_args, **server_kwargs) + server.start() + try: + func(server=server, *args, **kwargs) + finally: + server.stop() + return wrapped + return wrapper + +class PyPIServerTestCase(unittest.TestCase): + + def setUp(self): + super(PyPIServerTestCase, self).setUp() + self.pypi = PyPIServer() + self.pypi.start() + + def tearDown(self): + super(PyPIServerTestCase, self).tearDown() + self.pypi.stop() + +class PyPIServer(threading.Thread): + """PyPI Mocked server. + Provides a mocked version of the PyPI API's, to ease tests. + + Support serving static content and serving previously given text. + """ + + def __init__(self, test_static_path=None, + static_filesystem_paths=["default"], static_uri_paths=["simple"]): + """Initialize the server. + + static_uri_paths and static_base_path are parameters used to provides + respectively the http_paths to serve statically, and where to find the + matching files on the filesystem. + """ + threading.Thread.__init__(self) + self._run = True + self.httpd = HTTPServer(('', 0), PyPIRequestHandler) + self.httpd.RequestHandlerClass.log_request = lambda *_: None + self.httpd.RequestHandlerClass.pypi_server = self + self.address = (self.httpd.server_name, self.httpd.server_port) + self.request_queue = Queue.Queue() + self._requests = [] + self.default_response_status = 200 + self.default_response_headers = [('Content-type', 'text/plain')] + self.default_response_data = "hello" + + # initialize static paths / filesystems + self.static_uri_paths = static_uri_paths + if test_static_path is not None: + static_filesystem_paths.append(test_static_path) + self.static_filesystem_paths = [PYPI_DEFAULT_STATIC_PATH + "/" + path + for path in static_filesystem_paths] + + def run(self): + # loop because we can't stop it otherwise, for python < 2.6 + while self._run: + r, w, e = select.select([self.httpd], [], [], 0.5) + if r: + self.httpd.handle_request() + + def stop(self): + """self shutdown is not supported for python < 2.6""" + self._run = False + + def get_next_response(self): + return (self.default_response_status, + self.default_response_headers, + self.default_response_data) + + @property + def requests(self): + """Use this property to get all requests that have been made + to the server + """ + while True: + try: + self._requests.append(self.request_queue.get_nowait()) + except Queue.Empty: + break + return self._requests + + @property + def full_address(self): + return "http://%s:%s" % self.address + + +class PyPIRequestHandler(SimpleHTTPRequestHandler): + # we need to access the pypi server while serving the content + pypi_server = None + + def do_POST(self): + return self.serve_request() + def do_GET(self): + return self.serve_request() + def do_DELETE(self): + return self.serve_request() + def do_PUT(self): + return self.serve_request() + + def serve_request(self): + """Serve the content. + + Also record the requests to be accessed later. If trying to access an + url matching a static uri, serve static content, otherwise serve + what is provided by the `get_next_response` method. + """ + # record the request. Read the input only on PUT or POST requests + if self.command in ("PUT", "POST"): + if 'content-length' in self.headers.dict: + request_data = self.rfile.read( + int(self.headers['content-length'])) + else: + request_data = self.rfile.read() + elif self.command in ("GET", "DELETE"): + request_data = '' + + self.pypi_server.request_queue.put((self, request_data)) + + # serve the content from local disc if we request an URL beginning + # by a pattern defined in `static_paths` + url_parts = self.path.split("/") + if (len(url_parts) > 1 and + url_parts[1] in self.pypi_server.static_uri_paths): + data = None + # always take the last first. + fs_paths = [] + fs_paths.extend(self.pypi_server.static_filesystem_paths) + fs_paths.reverse() + relative_path = self.path + for fs_path in fs_paths: + try: + if self.path.endswith("/"): + relative_path += "index.html" + file = open(fs_path + relative_path) + data = file.read() + if relative_path.endswith('.tar.gz'): + headers=[('Content-type', 'application/x-gtar')] + else: + headers=[('Content-type', 'text/html')] + self.make_response(data, headers=headers) + except IOError: + pass + + if data is None: + self.make_response("Not found", 404) + + # otherwise serve the content from get_next_response + else: + # send back a response + status, headers, data = self.pypi_server.get_next_response() + self.make_response(data, status, headers) + + def make_response(self, data, status=200, + headers=[('Content-type', 'text/html')]): + """Send the response to the HTTP client""" + if not isinstance(status, int): + try: + status = int(status) + except ValueError: + # we probably got something like YYY Codename. + # Just get the first 3 digits + status = int(status[:3]) + + self.send_response(status) + for header, value in headers: + self.send_header(header, value) + self.end_headers() + self.wfile.write(data) diff --git a/src/distutils2/tests/pypiserver/downloads_with_md5/simple/badmd5/badmd5-0.1.tar.gz b/src/distutils2/tests/pypiserver/downloads_with_md5/simple/badmd5/badmd5-0.1.tar.gz new file mode 100644 diff --git a/src/distutils2/tests/pypiserver/downloads_with_md5/simple/badmd5/index.html b/src/distutils2/tests/pypiserver/downloads_with_md5/simple/badmd5/index.html new file mode 100644 --- /dev/null +++ b/src/distutils2/tests/pypiserver/downloads_with_md5/simple/badmd5/index.html @@ -0,0 +1,3 @@ + +badmd5-0.1.tar.gz
+ diff --git a/src/distutils2/tests/pypiserver/downloads_with_md5/simple/foobar/foobar-0.1.tar.gz b/src/distutils2/tests/pypiserver/downloads_with_md5/simple/foobar/foobar-0.1.tar.gz new file mode 100644 diff --git a/src/distutils2/tests/pypiserver/downloads_with_md5/simple/foobar/index.html b/src/distutils2/tests/pypiserver/downloads_with_md5/simple/foobar/index.html new file mode 100644 --- /dev/null +++ b/src/distutils2/tests/pypiserver/downloads_with_md5/simple/foobar/index.html @@ -0,0 +1,3 @@ + +foobar-0.1.tar.gz
+ diff --git a/src/distutils2/tests/pypiserver/downloads_with_md5/simple/index.html b/src/distutils2/tests/pypiserver/downloads_with_md5/simple/index.html new file mode 100644 --- /dev/null +++ b/src/distutils2/tests/pypiserver/downloads_with_md5/simple/index.html @@ -0,0 +1,2 @@ +foobar/ +badmd5/ diff --git a/src/distutils2/tests/pypiserver/foo_bar_baz/simple/bar/index.html b/src/distutils2/tests/pypiserver/foo_bar_baz/simple/bar/index.html new file mode 100644 --- /dev/null +++ b/src/distutils2/tests/pypiserver/foo_bar_baz/simple/bar/index.html @@ -0,0 +1,6 @@ +Links for bar

Links for bar

+bar-1.0.tar.gz
+bar-1.0.1.tar.gz
+bar-2.0.tar.gz
+bar-2.0.1.tar.gz
+ diff --git a/src/distutils2/tests/pypiserver/foo_bar_baz/simple/baz/index.html b/src/distutils2/tests/pypiserver/foo_bar_baz/simple/baz/index.html new file mode 100644 --- /dev/null +++ b/src/distutils2/tests/pypiserver/foo_bar_baz/simple/baz/index.html @@ -0,0 +1,6 @@ +Links for baz

Links for baz

+baz-1.0.tar.gz
+baz-1.0.1.tar.gz
+baz-2.0.tar.gz
+baz-2.0.1.tar.gz
+ diff --git a/src/distutils2/tests/pypiserver/foo_bar_baz/simple/foo/index.html b/src/distutils2/tests/pypiserver/foo_bar_baz/simple/foo/index.html new file mode 100644 --- /dev/null +++ b/src/distutils2/tests/pypiserver/foo_bar_baz/simple/foo/index.html @@ -0,0 +1,6 @@ +Links for foo

Links for foo

+foo-1.0.tar.gz
+foo-1.0.1.tar.gz
+foo-2.0.tar.gz
+foo-2.0.1.tar.gz
+ diff --git a/src/distutils2/tests/pypiserver/foo_bar_baz/simple/index.html b/src/distutils2/tests/pypiserver/foo_bar_baz/simple/index.html new file mode 100644 --- /dev/null +++ b/src/distutils2/tests/pypiserver/foo_bar_baz/simple/index.html @@ -0,0 +1,3 @@ +foo/ +bar/ +baz/ diff --git a/src/distutils2/tests/pypiserver/test_found_links/simple/foobar/index.html b/src/distutils2/tests/pypiserver/test_found_links/simple/foobar/index.html new file mode 100644 --- /dev/null +++ b/src/distutils2/tests/pypiserver/test_found_links/simple/foobar/index.html @@ -0,0 +1,6 @@ +Links for Foobar

Links for Foobar

+Foobar-1.0.tar.gz
+Foobar-1.0.1.tar.gz
+Foobar-2.0.tar.gz
+Foobar-2.0.1.tar.gz
+ diff --git a/src/distutils2/tests/pypiserver/test_found_links/simple/index.html b/src/distutils2/tests/pypiserver/test_found_links/simple/index.html new file mode 100644 --- /dev/null +++ b/src/distutils2/tests/pypiserver/test_found_links/simple/index.html @@ -0,0 +1,1 @@ +foobar/ diff --git a/src/distutils2/tests/pypiserver/test_pypi_server/external/index.html b/src/distutils2/tests/pypiserver/test_pypi_server/external/index.html new file mode 100644 --- /dev/null +++ b/src/distutils2/tests/pypiserver/test_pypi_server/external/index.html @@ -0,0 +1,1 @@ +index.html from external server diff --git a/src/distutils2/tests/pypiserver/test_pypi_server/simple/index.html b/src/distutils2/tests/pypiserver/test_pypi_server/simple/index.html new file mode 100644 --- /dev/null +++ b/src/distutils2/tests/pypiserver/test_pypi_server/simple/index.html @@ -0,0 +1,1 @@ +Yeah diff --git a/src/distutils2/tests/pypiserver/with_externals/external/external.html b/src/distutils2/tests/pypiserver/with_externals/external/external.html new file mode 100644 --- /dev/null +++ b/src/distutils2/tests/pypiserver/with_externals/external/external.html @@ -0,0 +1,3 @@ + +bad old link + diff --git a/src/distutils2/tests/pypiserver/with_externals/simple/foobar/index.html b/src/distutils2/tests/pypiserver/with_externals/simple/foobar/index.html new file mode 100644 --- /dev/null +++ b/src/distutils2/tests/pypiserver/with_externals/simple/foobar/index.html @@ -0,0 +1,4 @@ + +foobar-0.1.tar.gz
+external homepage
+ diff --git a/src/distutils2/tests/pypiserver/with_externals/simple/index.html b/src/distutils2/tests/pypiserver/with_externals/simple/index.html new file mode 100644 --- /dev/null +++ b/src/distutils2/tests/pypiserver/with_externals/simple/index.html @@ -0,0 +1,1 @@ +foobar/ diff --git a/src/distutils2/tests/pypiserver/with_norel_links/external/homepage.html b/src/distutils2/tests/pypiserver/with_norel_links/external/homepage.html new file mode 100644 --- /dev/null +++ b/src/distutils2/tests/pypiserver/with_norel_links/external/homepage.html @@ -0,0 +1,7 @@ + + +

a rel=homepage HTML page

+foobar 2.0 + + + diff --git a/src/distutils2/tests/pypiserver/with_norel_links/external/nonrel.html b/src/distutils2/tests/pypiserver/with_norel_links/external/nonrel.html new file mode 100644 --- /dev/null +++ b/src/distutils2/tests/pypiserver/with_norel_links/external/nonrel.html @@ -0,0 +1,1 @@ +A page linked without rel="download" or rel="homepage" link. diff --git a/src/distutils2/tests/pypiserver/with_norel_links/simple/foobar/index.html b/src/distutils2/tests/pypiserver/with_norel_links/simple/foobar/index.html new file mode 100644 --- /dev/null +++ b/src/distutils2/tests/pypiserver/with_norel_links/simple/foobar/index.html @@ -0,0 +1,6 @@ + +foobar-0.1.tar.gz
+external homepage
+unrelated link
+unrelated download
+ diff --git a/src/distutils2/tests/pypiserver/with_norel_links/simple/index.html b/src/distutils2/tests/pypiserver/with_norel_links/simple/index.html new file mode 100644 --- /dev/null +++ b/src/distutils2/tests/pypiserver/with_norel_links/simple/index.html @@ -0,0 +1,1 @@ +foobar/ diff --git a/src/distutils2/tests/pypiserver/with_real_externals/simple/foobar/index.html b/src/distutils2/tests/pypiserver/with_real_externals/simple/foobar/index.html new file mode 100644 --- /dev/null +++ b/src/distutils2/tests/pypiserver/with_real_externals/simple/foobar/index.html @@ -0,0 +1,4 @@ + +foobar-0.1.tar.gz
+external homepage
+ diff --git a/src/distutils2/tests/pypiserver/with_real_externals/simple/index.html b/src/distutils2/tests/pypiserver/with_real_externals/simple/index.html new file mode 100644 --- /dev/null +++ b/src/distutils2/tests/pypiserver/with_real_externals/simple/index.html @@ -0,0 +1,1 @@ +foobar/ diff --git a/src/distutils2/tests/support.py b/src/distutils2/tests/support.py --- a/src/distutils2/tests/support.py +++ b/src/distutils2/tests/support.py @@ -14,7 +14,6 @@ from distutils2 import log from distutils2.log import DEBUG, INFO, WARN, ERROR, FATAL -from distutils2.core import Distribution if sys.version_info >= (2, 7): # improved unittest package from 2.7's standard library @@ -42,7 +41,7 @@ def _log(self, level, msg, args): if level not in (DEBUG, INFO, WARN, ERROR, FATAL): - raise ValueError('%s wrong log level' % str(level)) + raise ValueError('%s wrong log level' % level) self.logs.append((level, msg, args)) def get_logs(self, *levels): @@ -65,12 +64,22 @@ def setUp(self): super(TempdirManager, self).setUp() self.tempdirs = [] + self.tempfiles = [] def tearDown(self): super(TempdirManager, self).tearDown() while self.tempdirs: d = self.tempdirs.pop() shutil.rmtree(d, os.name in ('nt', 'cygwin')) + for file_ in self.tempfiles: + if os.path.exists(file_): + os.remove(file_) + + def mktempfile(self): + """Create a temporary file that will be cleaned up.""" + tempfile_ = tempfile.NamedTemporaryFile() + self.tempfiles.append(tempfile_.name) + return tempfile_ def mkdtemp(self): """Create a temporary directory that will be cleaned up. @@ -105,6 +114,7 @@ It returns the package directory and the distribution instance. """ + from distutils2.dist import Distribution tmp_dir = self.mkdtemp() pkg_dir = os.path.join(tmp_dir, pkg_name) os.mkdir(pkg_dir) diff --git a/src/distutils2/tests/test_Mixin2to3.py b/src/distutils2/tests/test_Mixin2to3.py new file mode 100644 --- /dev/null +++ b/src/distutils2/tests/test_Mixin2to3.py @@ -0,0 +1,54 @@ +"""Tests for distutils.command.build_py.""" +import sys +import tempfile + +import distutils2 +from distutils2.tests import support +from distutils2.tests.support import unittest +from distutils2.command.build_py import Mixin2to3 + + +class Mixin2to3TestCase(support.TempdirManager, unittest.TestCase): + + @unittest.skipUnless(sys.version > '2.6', 'Need >= 2.6') + def test_convert_code_only(self): + # used to check if code gets converted properly. + code_content = "print 'test'\n" + code_handle = self.mktempfile() + code_name = code_handle.name + + code_handle.write(code_content) + code_handle.flush() + + mixin2to3 = Mixin2to3() + mixin2to3._run_2to3([code_name]) + converted_code_content = "print('test')\n" + new_code_content = "".join(open(code_name).readlines()) + + self.assertEquals(new_code_content, converted_code_content) + + @unittest.skipUnless(sys.version > '2.6', 'Need >= 2.6') + def test_doctests_only(self): + # used to check if doctests gets converted properly. + doctest_content = '"""\n>>> print test\ntest\n"""\nprint test\n\n' + doctest_handle = self.mktempfile() + doctest_name = doctest_handle.name + + doctest_handle.write(doctest_content) + doctest_handle.flush() + + mixin2to3 = Mixin2to3() + mixin2to3._run_2to3([doctest_name]) + + converted_doctest_content = ['"""', '>>> print(test)', 'test', '"""', + 'print(test)', '', '', ''] + converted_doctest_content = '\n'.join(converted_doctest_content) + new_doctest_content = "".join(open(doctest_name).readlines()) + + self.assertEquals(new_doctest_content, converted_doctest_content) + +def test_suite(): + return unittest.makeSuite(Mixin2to3TestCase) + +if __name__ == "__main__": + unittest.main(defaultTest="test_suite") diff --git a/src/distutils2/tests/test_bdist.py b/src/distutils2/tests/test_bdist.py --- a/src/distutils2/tests/test_bdist.py +++ b/src/distutils2/tests/test_bdist.py @@ -1,8 +1,6 @@ """Tests for distutils.command.bdist.""" import sys import os -import tempfile -import shutil from distutils2.tests import run_unittest @@ -25,7 +23,7 @@ cmd = bdist(dist) cmd.formats = ['msi'] cmd.ensure_finalized() - self.assertEquals(cmd.formats, ['msi']) + self.assertEqual(cmd.formats, ['msi']) # what format bdist offers ? # XXX an explicit list in bdist is @@ -33,9 +31,9 @@ # we should add a registry formats = ['zip', 'gztar', 'bztar', 'ztar', 'tar', 'wininst', 'msi'] formats.sort() - founded = cmd.format_command.keys() - founded.sort() - self.assertEquals(founded, formats) + found = cmd.format_command.keys() + found.sort() + self.assertEqual(found, formats) def test_suite(): return unittest.makeSuite(BuildTestCase) diff --git a/src/distutils2/tests/test_bdist_dumb.py b/src/distutils2/tests/test_bdist_dumb.py --- a/src/distutils2/tests/test_bdist_dumb.py +++ b/src/distutils2/tests/test_bdist_dumb.py @@ -78,7 +78,7 @@ base = base.replace(':', '-') wanted = ['%s.zip' % base] - self.assertEquals(dist_created, wanted) + self.assertEqual(dist_created, wanted) # now let's check what we have in the zip file # XXX to be done @@ -87,16 +87,16 @@ pkg_dir, dist = self.create_dist() os.chdir(pkg_dir) cmd = bdist_dumb(dist) - self.assertEquals(cmd.bdist_dir, None) + self.assertEqual(cmd.bdist_dir, None) cmd.finalize_options() # bdist_dir is initialized to bdist_base/dumb if not set base = cmd.get_finalized_command('bdist').bdist_base - self.assertEquals(cmd.bdist_dir, os.path.join(base, 'dumb')) + self.assertEqual(cmd.bdist_dir, os.path.join(base, 'dumb')) # the format is set to a default value depending on the os.name default = cmd.default_format[os.name] - self.assertEquals(cmd.format, default) + self.assertEqual(cmd.format, default) def test_suite(): return unittest.makeSuite(BuildDumbTestCase) diff --git a/src/distutils2/tests/test_build.py b/src/distutils2/tests/test_build.py --- a/src/distutils2/tests/test_build.py +++ b/src/distutils2/tests/test_build.py @@ -20,11 +20,11 @@ cmd.finalize_options() # if not specified, plat_name gets the current platform - self.assertEquals(cmd.plat_name, get_platform()) + self.assertEqual(cmd.plat_name, get_platform()) # build_purelib is build + lib wanted = os.path.join(cmd.build_base, 'lib') - self.assertEquals(cmd.build_purelib, wanted) + self.assertEqual(cmd.build_purelib, wanted) # build_platlib is 'build/lib.platform-x.x[-pydebug]' # examples: @@ -34,21 +34,21 @@ self.assertTrue(cmd.build_platlib.endswith('-pydebug')) plat_spec += '-pydebug' wanted = os.path.join(cmd.build_base, 'lib' + plat_spec) - self.assertEquals(cmd.build_platlib, wanted) + self.assertEqual(cmd.build_platlib, wanted) # by default, build_lib = build_purelib - self.assertEquals(cmd.build_lib, cmd.build_purelib) + self.assertEqual(cmd.build_lib, cmd.build_purelib) # build_temp is build/temp. wanted = os.path.join(cmd.build_base, 'temp' + plat_spec) - self.assertEquals(cmd.build_temp, wanted) + self.assertEqual(cmd.build_temp, wanted) # build_scripts is build/scripts-x.x wanted = os.path.join(cmd.build_base, 'scripts-' + sys.version[0:3]) - self.assertEquals(cmd.build_scripts, wanted) + self.assertEqual(cmd.build_scripts, wanted) # executable is os.path.normpath(sys.executable) - self.assertEquals(cmd.executable, os.path.normpath(sys.executable)) + self.assertEqual(cmd.executable, os.path.normpath(sys.executable)) def test_suite(): return unittest.makeSuite(BuildTestCase) diff --git a/src/distutils2/tests/test_build_clib.py b/src/distutils2/tests/test_build_clib.py --- a/src/distutils2/tests/test_build_clib.py +++ b/src/distutils2/tests/test_build_clib.py @@ -55,14 +55,14 @@ self.assertRaises(DistutilsSetupError, cmd.get_source_files) cmd.libraries = [('name', {'sources': ['a', 'b']})] - self.assertEquals(cmd.get_source_files(), ['a', 'b']) + self.assertEqual(cmd.get_source_files(), ['a', 'b']) cmd.libraries = [('name', {'sources': ('a', 'b')})] - self.assertEquals(cmd.get_source_files(), ['a', 'b']) + self.assertEqual(cmd.get_source_files(), ['a', 'b']) cmd.libraries = [('name', {'sources': ('a', 'b')}), ('name2', {'sources': ['c', 'd']})] - self.assertEquals(cmd.get_source_files(), ['a', 'b', 'c', 'd']) + self.assertEqual(cmd.get_source_files(), ['a', 'b', 'c', 'd']) def test_build_libraries(self): @@ -91,11 +91,11 @@ cmd.include_dirs = 'one-dir' cmd.finalize_options() - self.assertEquals(cmd.include_dirs, ['one-dir']) + self.assertEqual(cmd.include_dirs, ['one-dir']) cmd.include_dirs = None cmd.finalize_options() - self.assertEquals(cmd.include_dirs, []) + self.assertEqual(cmd.include_dirs, []) cmd.distribution.libraries = 'WONTWORK' self.assertRaises(DistutilsSetupError, cmd.finalize_options) diff --git a/src/distutils2/tests/test_build_ext.py b/src/distutils2/tests/test_build_ext.py --- a/src/distutils2/tests/test_build_ext.py +++ b/src/distutils2/tests/test_build_ext.py @@ -1,6 +1,5 @@ import sys import os -import tempfile import shutil from StringIO import StringIO import warnings @@ -81,11 +80,11 @@ for attr in ('error', 'foo', 'new', 'roj'): self.assertTrue(hasattr(xx, attr)) - self.assertEquals(xx.foo(2, 5), 7) - self.assertEquals(xx.foo(13,15), 28) - self.assertEquals(xx.new().demo(), None) + self.assertEqual(xx.foo(2, 5), 7) + self.assertEqual(xx.foo(13,15), 28) + self.assertEqual(xx.new().demo(), None) doc = 'This is a template module just for instruction.' - self.assertEquals(xx.__doc__, doc) + self.assertEqual(xx.__doc__, doc) self.assertTrue(isinstance(xx.Null(), xx.Null)) self.assertTrue(isinstance(xx.Str(), xx.Str)) @@ -195,7 +194,7 @@ cmd = build_ext(dist) cmd.libraries = 'my_lib' cmd.finalize_options() - self.assertEquals(cmd.libraries, ['my_lib']) + self.assertEqual(cmd.libraries, ['my_lib']) # make sure cmd.library_dirs is turned into a list # if it's a string @@ -209,7 +208,7 @@ cmd = build_ext(dist) cmd.rpath = os.pathsep.join(['one', 'two']) cmd.finalize_options() - self.assertEquals(cmd.rpath, ['one', 'two']) + self.assertEqual(cmd.rpath, ['one', 'two']) # XXX more tests to perform for win32 @@ -218,79 +217,32 @@ cmd = build_ext(dist) cmd.define = 'one,two' cmd.finalize_options() - self.assertEquals(cmd.define, [('one', '1'), ('two', '1')]) + self.assertEqual(cmd.define, [('one', '1'), ('two', '1')]) # make sure undef is turned into a list of # strings if they are ','-separated strings cmd = build_ext(dist) cmd.undef = 'one,two' cmd.finalize_options() - self.assertEquals(cmd.undef, ['one', 'two']) + self.assertEqual(cmd.undef, ['one', 'two']) # make sure swig_opts is turned into a list cmd = build_ext(dist) cmd.swig_opts = None cmd.finalize_options() - self.assertEquals(cmd.swig_opts, []) + self.assertEqual(cmd.swig_opts, []) cmd = build_ext(dist) cmd.swig_opts = '1 2' cmd.finalize_options() - self.assertEquals(cmd.swig_opts, ['1', '2']) - - def test_check_extensions_list(self): - dist = Distribution() - cmd = build_ext(dist) - cmd.finalize_options() - - #'extensions' option must be a list of Extension instances - self.assertRaises(DistutilsSetupError, cmd.check_extensions_list, 'foo') - - # each element of 'ext_modules' option must be an - # Extension instance or 2-tuple - exts = [('bar', 'foo', 'bar'), 'foo'] - self.assertRaises(DistutilsSetupError, cmd.check_extensions_list, exts) - - # first element of each tuple in 'ext_modules' - # must be the extension name (a string) and match - # a python dotted-separated name - exts = [('foo-bar', '')] - self.assertRaises(DistutilsSetupError, cmd.check_extensions_list, exts) - - # second element of each tuple in 'ext_modules' - # must be a ary (build info) - exts = [('foo.bar', '')] - self.assertRaises(DistutilsSetupError, cmd.check_extensions_list, exts) - - # ok this one should pass - exts = [('foo.bar', {'sources': [''], 'libraries': 'foo', - 'some': 'bar'})] - cmd.check_extensions_list(exts) - ext = exts[0] - self.assertTrue(isinstance(ext, Extension)) - - # check_extensions_list adds in ext the values passed - # when they are in ('include_dirs', 'library_dirs', 'libraries' - # 'extra_objects', 'extra_compile_args', 'extra_link_args') - self.assertEquals(ext.libraries, 'foo') - self.assertTrue(not hasattr(ext, 'some')) - - # 'macros' element of build info dict must be 1- or 2-tuple - exts = [('foo.bar', {'sources': [''], 'libraries': 'foo', - 'some': 'bar', 'macros': [('1', '2', '3'), 'foo']})] - self.assertRaises(DistutilsSetupError, cmd.check_extensions_list, exts) - - exts[0][1]['macros'] = [('1', '2'), ('3',)] - cmd.check_extensions_list(exts) - self.assertEquals(exts[0].undef_macros, ['3']) - self.assertEquals(exts[0].define_macros, [('1', '2')]) + self.assertEqual(cmd.swig_opts, ['1', '2']) def test_get_source_files(self): modules = [Extension('foo', ['xxx'], optional=False)] dist = Distribution({'name': 'xx', 'ext_modules': modules}) cmd = build_ext(dist) cmd.ensure_finalized() - self.assertEquals(cmd.get_source_files(), ['xxx']) + self.assertEqual(cmd.get_source_files(), ['xxx']) def test_compiler_option(self): # cmd.compiler is an option and @@ -301,7 +253,7 @@ cmd.compiler = 'unix' cmd.ensure_finalized() cmd.run() - self.assertEquals(cmd.compiler, 'unix') + self.assertEqual(cmd.compiler, 'unix') def test_get_outputs(self): tmp_dir = self.mkdtemp() @@ -312,7 +264,7 @@ 'ext_modules': [ext]}) cmd = build_ext(dist) cmd.ensure_finalized() - self.assertEquals(len(cmd.get_outputs()), 1) + self.assertEqual(len(cmd.get_outputs()), 1) if os.name == "nt": cmd.debug = sys.executable.endswith("_d.exe") @@ -332,19 +284,19 @@ finally: os.chdir(old_wd) self.assertTrue(os.path.exists(so_file)) - self.assertEquals(os.path.splitext(so_file)[-1], + self.assertEqual(os.path.splitext(so_file)[-1], sysconfig.get_config_var('SO')) so_dir = os.path.dirname(so_file) - self.assertEquals(so_dir, other_tmp_dir) + self.assertEqual(so_dir, other_tmp_dir) cmd.inplace = 0 cmd.run() so_file = cmd.get_outputs()[0] self.assertTrue(os.path.exists(so_file)) - self.assertEquals(os.path.splitext(so_file)[-1], + self.assertEqual(os.path.splitext(so_file)[-1], sysconfig.get_config_var('SO')) so_dir = os.path.dirname(so_file) - self.assertEquals(so_dir, cmd.build_lib) + self.assertEqual(so_dir, cmd.build_lib) # inplace = 0, cmd.package = 'bar' build_py = cmd.get_finalized_command('build_py') @@ -352,7 +304,7 @@ path = cmd.get_ext_fullpath('foo') # checking that the last directory is the build_dir path = os.path.split(path)[0] - self.assertEquals(path, cmd.build_lib) + self.assertEqual(path, cmd.build_lib) # inplace = 1, cmd.package = 'bar' cmd.inplace = 1 @@ -366,7 +318,7 @@ # checking that the last directory is bar path = os.path.split(path)[0] lastdir = os.path.split(path)[-1] - self.assertEquals(lastdir, 'bar') + self.assertEqual(lastdir, 'bar') def test_ext_fullpath(self): ext = sysconfig.get_config_vars()['SO'] @@ -382,14 +334,14 @@ curdir = os.getcwd() wanted = os.path.join(curdir, 'src', 'lxml', 'etree' + ext) path = cmd.get_ext_fullpath('lxml.etree') - self.assertEquals(wanted, path) + self.assertEqual(wanted, path) # building lxml.etree not inplace cmd.inplace = 0 cmd.build_lib = os.path.join(curdir, 'tmpdir') wanted = os.path.join(curdir, 'tmpdir', 'lxml', 'etree' + ext) path = cmd.get_ext_fullpath('lxml.etree') - self.assertEquals(wanted, path) + self.assertEqual(wanted, path) # building twisted.runner.portmap not inplace build_py = cmd.get_finalized_command('build_py') @@ -398,13 +350,13 @@ path = cmd.get_ext_fullpath('twisted.runner.portmap') wanted = os.path.join(curdir, 'tmpdir', 'twisted', 'runner', 'portmap' + ext) - self.assertEquals(wanted, path) + self.assertEqual(wanted, path) # building twisted.runner.portmap inplace cmd.inplace = 1 path = cmd.get_ext_fullpath('twisted.runner.portmap') wanted = os.path.join(curdir, 'twisted', 'runner', 'portmap' + ext) - self.assertEquals(wanted, path) + self.assertEqual(wanted, path) def test_suite(): src = _get_source_filename() diff --git a/src/distutils2/tests/test_build_py.py b/src/distutils2/tests/test_build_py.py --- a/src/distutils2/tests/test_build_py.py +++ b/src/distutils2/tests/test_build_py.py @@ -19,11 +19,15 @@ def test_package_data(self): sources = self.mkdtemp() f = open(os.path.join(sources, "__init__.py"), "w") - f.write("# Pretend this is a package.") - f.close() + try: + f.write("# Pretend this is a package.") + finally: + f.close() f = open(os.path.join(sources, "README.txt"), "w") - f.write("Info about this package") - f.close() + try: + f.write("Info about this package") + finally: + f.close() destination = self.mkdtemp() diff --git a/src/distutils2/tests/test_build_scripts.py b/src/distutils2/tests/test_build_scripts.py --- a/src/distutils2/tests/test_build_scripts.py +++ b/src/distutils2/tests/test_build_scripts.py @@ -74,8 +74,10 @@ def write_script(self, dir, name, text): f = open(os.path.join(dir, name), "w") - f.write(text) - f.close() + try: + f.write(text) + finally: + f.close() def test_version_int(self): source = self.mkdtemp() diff --git a/src/distutils2/tests/test_ccompiler.py b/src/distutils2/tests/test_ccompiler.py --- a/src/distutils2/tests/test_ccompiler.py +++ b/src/distutils2/tests/test_ccompiler.py @@ -31,7 +31,7 @@ opts = gen_lib_options(compiler, libdirs, runlibdirs, libs) wanted = ['-Llib1', '-Llib2', '-cool', '-Rrunlib1', 'found', '-lname2'] - self.assertEquals(opts, wanted) + self.assertEqual(opts, wanted) def test_customize_compiler(self): @@ -51,7 +51,7 @@ comp = compiler() customize_compiler(comp) - self.assertEquals(comp.exes['archiver'], 'my_ar -arflags') + self.assertEqual(comp.exes['archiver'], 'my_ar -arflags') def test_suite(): return unittest.makeSuite(CCompilerTestCase) diff --git a/src/distutils2/tests/test_check.py b/src/distutils2/tests/test_check.py --- a/src/distutils2/tests/test_check.py +++ b/src/distutils2/tests/test_check.py @@ -37,7 +37,7 @@ 'name': 'xxx', 'version': 'xxx' } cmd = self._run(metadata) - self.assertEquals(len(cmd._warnings), 0) + self.assertEqual(len(cmd._warnings), 0) # now with the strict mode, we should # get an error if there are missing metadata @@ -45,7 +45,7 @@ # and of course, no error when all metadata are present cmd = self._run(metadata, strict=1) - self.assertEquals(len(cmd._warnings), 0) + self.assertEqual(len(cmd._warnings), 0) def test_check_restructuredtext(self): if not _HAS_DOCUTILS: # won't test without docutils @@ -55,7 +55,7 @@ pkg_info, dist = self.create_dist(description=broken_rest) cmd = check(dist) cmd.check_restructuredtext() - self.assertEquals(len(cmd._warnings), 1) + self.assertEqual(len(cmd._warnings), 1) # let's see if we have an error with strict=1 metadata = {'home_page': 'xxx', 'author': 'xxx', @@ -69,7 +69,7 @@ # and non-broken rest metadata['description'] = 'title\n=====\n\ntest' cmd = self._run(metadata, strict=1, restructuredtext=1) - self.assertEquals(len(cmd._warnings), 0) + self.assertEqual(len(cmd._warnings), 0) def test_check_all(self): diff --git a/src/distutils2/tests/test_cmd.py b/src/distutils2/tests/test_cmd.py --- a/src/distutils2/tests/test_cmd.py +++ b/src/distutils2/tests/test_cmd.py @@ -43,7 +43,7 @@ # making sure execute gets called properly def _execute(func, args, exec_msg, level): - self.assertEquals(exec_msg, 'generating out from in') + self.assertEqual(exec_msg, 'generating out from in') cmd.force = True cmd.execute = _execute cmd.make_file(infiles='in', outfile='out', func='func', args=()) @@ -62,7 +62,7 @@ wanted = ["command options for 'MyCmd':", ' option1 = 1', ' option2 = 1'] - self.assertEquals(msgs, wanted) + self.assertEqual(msgs, wanted) def test_ensure_string(self): cmd = self.cmd @@ -80,7 +80,7 @@ cmd = self.cmd cmd.option1 = 'ok,dok' cmd.ensure_string_list('option1') - self.assertEquals(cmd.option1, ['ok', 'dok']) + self.assertEqual(cmd.option1, ['ok', 'dok']) cmd.option2 = ['xxx', 'www'] cmd.ensure_string_list('option2') diff --git a/src/distutils2/tests/test_config.py b/src/distutils2/tests/test_config.py --- a/src/distutils2/tests/test_config.py +++ b/src/distutils2/tests/test_config.py @@ -1,7 +1,6 @@ """Tests for distutils.pypirc.pypirc.""" import sys import os -import tempfile import shutil from distutils2.core import PyPIRCCommand @@ -87,20 +86,20 @@ config = config.items() config.sort() - waited = [('password', 'secret'), ('realm', 'pypi'), - ('repository', 'http://pypi.python.org/pypi'), - ('server', 'server1'), ('username', 'me')] - self.assertEquals(config, waited) + expected = [('password', 'secret'), ('realm', 'pypi'), + ('repository', 'http://pypi.python.org/pypi'), + ('server', 'server1'), ('username', 'me')] + self.assertEqual(config, expected) # old format self.write_file(self.rc, PYPIRC_OLD) config = cmd._read_pypirc() config = config.items() config.sort() - waited = [('password', 'secret'), ('realm', 'pypi'), - ('repository', 'http://pypi.python.org/pypi'), - ('server', 'server-login'), ('username', 'tarek')] - self.assertEquals(config, waited) + expected = [('password', 'secret'), ('realm', 'pypi'), + ('repository', 'http://pypi.python.org/pypi'), + ('server', 'server-login'), ('username', 'tarek')] + self.assertEqual(config, expected) def test_server_empty_registration(self): cmd = self._cmd(self.dist) @@ -109,7 +108,7 @@ cmd._store_pypirc('tarek', 'xxx') self.assertTrue(os.path.exists(rc)) content = open(rc).read() - self.assertEquals(content, WANTED) + self.assertEqual(content, WANTED) def test_suite(): return unittest.makeSuite(PyPIRCCommandTestCase) diff --git a/src/distutils2/tests/test_config_cmd.py b/src/distutils2/tests/test_config_cmd.py --- a/src/distutils2/tests/test_config_cmd.py +++ b/src/distutils2/tests/test_config_cmd.py @@ -34,7 +34,7 @@ f.close() dump_file(this_file, 'I am the header') - self.assertEquals(len(self._logs), numlines+1) + self.assertEqual(len(self._logs), numlines+1) def test_search_cpp(self): if sys.platform == 'win32': @@ -44,10 +44,10 @@ # simple pattern searches match = cmd.search_cpp(pattern='xxx', body='// xxx') - self.assertEquals(match, 0) + self.assertEqual(match, 0) match = cmd.search_cpp(pattern='_configtest', body='// xxx') - self.assertEquals(match, 1) + self.assertEqual(match, 1) def test_finalize_options(self): # finalize_options does a bit of transformation @@ -59,9 +59,9 @@ cmd.library_dirs = 'three%sfour' % os.pathsep cmd.ensure_finalized() - self.assertEquals(cmd.include_dirs, ['one', 'two']) - self.assertEquals(cmd.libraries, ['one']) - self.assertEquals(cmd.library_dirs, ['three', 'four']) + self.assertEqual(cmd.include_dirs, ['one', 'two']) + self.assertEqual(cmd.libraries, ['one']) + self.assertEqual(cmd.library_dirs, ['three', 'four']) def test_clean(self): # _clean removes files diff --git a/src/distutils2/tests/test_converter.py b/src/distutils2/tests/test_converter.py --- a/src/distutils2/tests/test_converter.py +++ b/src/distutils2/tests/test_converter.py @@ -30,7 +30,7 @@ wanted = file_.replace('before', 'after') wanted = _read_file(os.path.join(convdir, wanted)) res = ref.refactor_string(original, 'setup.py') - self.assertEquals(str(res), wanted) + self.assertEqual(str(res), wanted) def test_suite(): return unittest.makeSuite(ConverterTestCase) diff --git a/src/distutils2/tests/test_cygwinccompiler.py b/src/distutils2/tests/test_cygwinccompiler.py --- a/src/distutils2/tests/test_cygwinccompiler.py +++ b/src/distutils2/tests/test_cygwinccompiler.py @@ -44,48 +44,48 @@ sys.version = ('2.6.1 (r261:67515, Dec 6 2008, 16:42:21) \n[GCC ' '4.0.1 (Apple Computer, Inc. build 5370)]') - self.assertEquals(check_config_h()[0], CONFIG_H_OK) + self.assertEqual(check_config_h()[0], CONFIG_H_OK) # then it tries to see if it can find "__GNUC__" in pyconfig.h sys.version = 'something without the *CC word' # if the file doesn't exist it returns CONFIG_H_UNCERTAIN - self.assertEquals(check_config_h()[0], CONFIG_H_UNCERTAIN) + self.assertEqual(check_config_h()[0], CONFIG_H_UNCERTAIN) # if it exists but does not contain __GNUC__, it returns CONFIG_H_NOTOK self.write_file(self.python_h, 'xxx') - self.assertEquals(check_config_h()[0], CONFIG_H_NOTOK) + self.assertEqual(check_config_h()[0], CONFIG_H_NOTOK) # and CONFIG_H_OK if __GNUC__ is found self.write_file(self.python_h, 'xxx __GNUC__ xxx') - self.assertEquals(check_config_h()[0], CONFIG_H_OK) + self.assertEqual(check_config_h()[0], CONFIG_H_OK) def test_get_msvcr(self): # none sys.version = ('2.6.1 (r261:67515, Dec 6 2008, 16:42:21) ' '\n[GCC 4.0.1 (Apple Computer, Inc. build 5370)]') - self.assertEquals(get_msvcr(), None) + self.assertEqual(get_msvcr(), None) # MSVC 7.0 sys.version = ('2.5.1 (r251:54863, Apr 18 2007, 08:51:08) ' '[MSC v.1300 32 bits (Intel)]') - self.assertEquals(get_msvcr(), ['msvcr70']) + self.assertEqual(get_msvcr(), ['msvcr70']) # MSVC 7.1 sys.version = ('2.5.1 (r251:54863, Apr 18 2007, 08:51:08) ' '[MSC v.1310 32 bits (Intel)]') - self.assertEquals(get_msvcr(), ['msvcr71']) + self.assertEqual(get_msvcr(), ['msvcr71']) # VS2005 / MSVC 8.0 sys.version = ('2.5.1 (r251:54863, Apr 18 2007, 08:51:08) ' '[MSC v.1400 32 bits (Intel)]') - self.assertEquals(get_msvcr(), ['msvcr80']) + self.assertEqual(get_msvcr(), ['msvcr80']) # VS2008 / MSVC 9.0 sys.version = ('2.5.1 (r251:54863, Apr 18 2007, 08:51:08) ' '[MSC v.1500 32 bits (Intel)]') - self.assertEquals(get_msvcr(), ['msvcr90']) + self.assertEqual(get_msvcr(), ['msvcr90']) # unknown sys.version = ('2.5.1 (r251:54863, Apr 18 2007, 08:51:08) ' diff --git a/src/distutils2/tests/test_depgraph.py b/src/distutils2/tests/test_depgraph.py --- a/src/distutils2/tests/test_depgraph.py +++ b/src/distutils2/tests/test_depgraph.py @@ -27,7 +27,7 @@ self.assertListEqual(sorted(l1), sorted(l2)) def setUp(self): - super(unittest.TestCase, self).setUp() + super(DepGraphTestCase, self).setUp() path = os.path.join(os.path.dirname(__file__), '..', '_backport', 'tests', 'fake_dists') path = os.path.abspath(path) @@ -177,7 +177,7 @@ self.checkLists(matches, expected) def tearDown(self): - super(unittest.TestCase, self).tearDown() + super(DepGraphTestCase, self).tearDown() sys.path = self.sys_path def test_suite(): diff --git a/src/distutils2/tests/test_dist.py b/src/distutils2/tests/test_dist.py --- a/src/distutils2/tests/test_dist.py +++ b/src/distutils2/tests/test_dist.py @@ -71,11 +71,11 @@ sys.argv.append("build") __, stdout = captured_stdout(self.create_distribution, files) - self.assertEquals(stdout, '') + self.assertEqual(stdout, '') distutils2.dist.DEBUG = True try: __, stdout = captured_stdout(self.create_distribution, files) - self.assertEquals(stdout, '') + self.assertEqual(stdout, '') finally: distutils2.dist.DEBUG = False @@ -129,13 +129,13 @@ # Check DistributionMetadata handling of Unicode fields tmp_dir = self.mkdtemp() my_file = os.path.join(tmp_dir, 'f') - klass = Distribution + cls = Distribution - dist = klass(attrs={'author': u'Mister Caf??', - 'name': 'my.package', - 'maintainer': u'Caf?? Junior', - 'summary': u'Caf?? torr??fi??', - 'description': u'H??h??h??'}) + dist = cls(attrs={'author': u'Mister Caf??', + 'name': 'my.package', + 'maintainer': u'Caf?? Junior', + 'summary': u'Caf?? torr??fi??', + 'description': u'H??h??h??'}) # let's make sure the file can be written @@ -144,11 +144,11 @@ dist.metadata.write_file(open(my_file, 'w')) # regular ascii is of course always usable - dist = klass(attrs={'author': 'Mister Cafe', - 'name': 'my.package', - 'maintainer': 'Cafe Junior', - 'summary': 'Cafe torrefie', - 'description': 'Hehehe'}) + dist = cls(attrs={'author': 'Mister Cafe', + 'name': 'my.package', + 'maintainer': 'Cafe Junior', + 'summary': 'Cafe torrefie', + 'description': 'Hehehe'}) my_file2 = os.path.join(tmp_dir, 'f2') dist.metadata.write_file(open(my_file, 'w')) @@ -156,7 +156,7 @@ def test_empty_options(self): # an empty options dictionary should not stay in the # list of attributes - klass = Distribution + cls = Distribution # catching warnings warns = [] @@ -166,15 +166,15 @@ old_warn = warnings.warn warnings.warn = _warn try: - dist = klass(attrs={'author': 'xxx', - 'name': 'xxx', - 'version': 'xxx', - 'url': 'xxxx', - 'options': {}}) + dist = cls(attrs={'author': 'xxx', + 'name': 'xxx', + 'version': 'xxx', + 'url': 'xxxx', + 'options': {}}) finally: warnings.warn = old_warn - self.assertEquals(len(warns), 0) + self.assertEqual(len(warns), 0) def test_finalize_options(self): @@ -185,20 +185,20 @@ dist.finalize_options() # finalize_option splits platforms and keywords - self.assertEquals(dist.metadata['platform'], ['one', 'two']) - self.assertEquals(dist.metadata['keywords'], ['one', 'two']) + self.assertEqual(dist.metadata['platform'], ['one', 'two']) + self.assertEqual(dist.metadata['keywords'], ['one', 'two']) def test_get_command_packages(self): dist = Distribution() - self.assertEquals(dist.command_packages, None) + self.assertEqual(dist.command_packages, None) cmds = dist.get_command_packages() - self.assertEquals(cmds, ['distutils2.command']) - self.assertEquals(dist.command_packages, + self.assertEqual(cmds, ['distutils2.command']) + self.assertEqual(dist.command_packages, ['distutils2.command']) dist.command_packages = 'one,two' cmds = dist.get_command_packages() - self.assertEquals(cmds, ['distutils2.command', 'one', 'two']) + self.assertEqual(cmds, ['distutils2.command', 'one', 'two']) def test_announce(self): @@ -238,7 +238,7 @@ os.path.expanduser = old_expander # make sure --no-user-cfg disables the user cfg file - self.assertEquals(len(all_files)-1, len(files)) + self.assertEqual(len(all_files)-1, len(files)) class MetadataTestCase(support.TempdirManager, support.EnvironGuard, @@ -340,8 +340,10 @@ temp_dir = self.mkdtemp() user_filename = os.path.join(temp_dir, user_filename) f = open(user_filename, 'w') - f.write('.') - f.close() + try: + f.write('.') + finally: + f.close() try: dist = Distribution() @@ -365,8 +367,8 @@ def test_fix_help_options(self): help_tuples = [('a', 'b', 'c', 'd'), (1, 2, 3, 4)] fancy_options = fix_help_options(help_tuples) - self.assertEquals(fancy_options[0], ('a', 'b', 'c')) - self.assertEquals(fancy_options[1], (1, 2, 3)) + self.assertEqual(fancy_options[0], ('a', 'b', 'c')) + self.assertEqual(fancy_options[1], (1, 2, 3)) def test_show_help(self): # smoke test, just makes sure some help is displayed @@ -412,14 +414,14 @@ PKG_INFO.seek(0) metadata.read_file(PKG_INFO) - self.assertEquals(metadata['name'], "package") - self.assertEquals(metadata['version'], "1.0") - self.assertEquals(metadata['summary'], "xxx") - self.assertEquals(metadata['download_url'], 'http://example.com') - self.assertEquals(metadata['keywords'], ['one', 'two']) - self.assertEquals(metadata['platform'], []) - self.assertEquals(metadata['obsoletes'], []) - self.assertEquals(metadata['requires-dist'], ['foo']) + self.assertEqual(metadata['name'], "package") + self.assertEqual(metadata['version'], "1.0") + self.assertEqual(metadata['summary'], "xxx") + self.assertEqual(metadata['download_url'], 'http://example.com') + self.assertEqual(metadata['keywords'], ['one', 'two']) + self.assertEqual(metadata['platform'], []) + self.assertEqual(metadata['obsoletes'], []) + self.assertEqual(metadata['requires-dist'], ['foo']) def test_suite(): suite = unittest.TestSuite() diff --git a/src/distutils2/tests/test_install.py b/src/distutils2/tests/test_install.py --- a/src/distutils2/tests/test_install.py +++ b/src/distutils2/tests/test_install.py @@ -139,23 +139,23 @@ # two elements cmd.handle_extra_path() - self.assertEquals(cmd.extra_path, ['path', 'dirs']) - self.assertEquals(cmd.extra_dirs, 'dirs') - self.assertEquals(cmd.path_file, 'path') + self.assertEqual(cmd.extra_path, ['path', 'dirs']) + self.assertEqual(cmd.extra_dirs, 'dirs') + self.assertEqual(cmd.path_file, 'path') # one element cmd.extra_path = ['path'] cmd.handle_extra_path() - self.assertEquals(cmd.extra_path, ['path']) - self.assertEquals(cmd.extra_dirs, 'path') - self.assertEquals(cmd.path_file, 'path') + self.assertEqual(cmd.extra_path, ['path']) + self.assertEqual(cmd.extra_dirs, 'path') + self.assertEqual(cmd.path_file, 'path') # none dist.extra_path = cmd.extra_path = None cmd.handle_extra_path() - self.assertEquals(cmd.extra_path, None) - self.assertEquals(cmd.extra_dirs, '') - self.assertEquals(cmd.path_file, None) + self.assertEqual(cmd.extra_path, None) + self.assertEqual(cmd.extra_dirs, '') + self.assertEqual(cmd.path_file, None) # three elements (no way !) cmd.extra_path = 'path,dirs,again' @@ -199,7 +199,7 @@ # line (the egg info file) f = open(cmd.record) try: - self.assertEquals(len(f.readlines()), 1) + self.assertEqual(len(f.readlines()), 1) finally: f.close() diff --git a/src/distutils2/tests/test_install_data.py b/src/distutils2/tests/test_install_data.py --- a/src/distutils2/tests/test_install_data.py +++ b/src/distutils2/tests/test_install_data.py @@ -27,14 +27,14 @@ self.write_file(two, 'xxx') cmd.data_files = [one, (inst2, [two])] - self.assertEquals(cmd.get_inputs(), [one, (inst2, [two])]) + self.assertEqual(cmd.get_inputs(), [one, (inst2, [two])]) # let's run the command cmd.ensure_finalized() cmd.run() # let's check the result - self.assertEquals(len(cmd.get_outputs()), 2) + self.assertEqual(len(cmd.get_outputs()), 2) rtwo = os.path.split(two)[-1] self.assertTrue(os.path.exists(os.path.join(inst2, rtwo))) rone = os.path.split(one)[-1] @@ -47,7 +47,7 @@ cmd.run() # let's check the result - self.assertEquals(len(cmd.get_outputs()), 2) + self.assertEqual(len(cmd.get_outputs()), 2) self.assertTrue(os.path.exists(os.path.join(inst2, rtwo))) self.assertTrue(os.path.exists(os.path.join(inst, rone))) cmd.outfiles = [] @@ -65,7 +65,7 @@ cmd.run() # let's check the result - self.assertEquals(len(cmd.get_outputs()), 4) + self.assertEqual(len(cmd.get_outputs()), 4) self.assertTrue(os.path.exists(os.path.join(inst2, rtwo))) self.assertTrue(os.path.exists(os.path.join(inst, rone))) diff --git a/src/distutils2/tests/test_install_headers.py b/src/distutils2/tests/test_install_headers.py --- a/src/distutils2/tests/test_install_headers.py +++ b/src/distutils2/tests/test_install_headers.py @@ -23,7 +23,7 @@ pkg_dir, dist = self.create_dist(headers=headers) cmd = install_headers(dist) - self.assertEquals(cmd.get_inputs(), headers) + self.assertEqual(cmd.get_inputs(), headers) # let's run the command cmd.install_dir = os.path.join(pkg_dir, 'inst') @@ -31,7 +31,7 @@ cmd.run() # let's check the results - self.assertEquals(len(cmd.get_outputs()), 2) + self.assertEqual(len(cmd.get_outputs()), 2) def test_suite(): return unittest.makeSuite(InstallHeadersTestCase) diff --git a/src/distutils2/tests/test_install_lib.py b/src/distutils2/tests/test_install_lib.py --- a/src/distutils2/tests/test_install_lib.py +++ b/src/distutils2/tests/test_install_lib.py @@ -25,8 +25,8 @@ cmd = install_lib(dist) cmd.finalize_options() - self.assertEquals(cmd.compile, 1) - self.assertEquals(cmd.optimize, 0) + self.assertEqual(cmd.compile, 1) + self.assertEqual(cmd.optimize, 0) # optimize must be 0, 1, or 2 cmd.optimize = 'foo' @@ -36,7 +36,7 @@ cmd.optimize = '2' cmd.finalize_options() - self.assertEquals(cmd.optimize, 2) + self.assertEqual(cmd.optimize, 2) @unittest.skipIf(no_bytecode, 'byte-compile not supported') def test_byte_compile(self): @@ -82,7 +82,7 @@ cmd.distribution.script_name = 'setup.py' # get_input should return 2 elements - self.assertEquals(len(cmd.get_inputs()), 2) + self.assertEqual(len(cmd.get_inputs()), 2) @unittest.skipUnless(bytecode_support, 'sys.dont_write_bytecode not supported') def test_dont_write_bytecode(self): diff --git a/src/distutils2/tests/test_install_scripts.py b/src/distutils2/tests/test_install_scripts.py --- a/src/distutils2/tests/test_install_scripts.py +++ b/src/distutils2/tests/test_install_scripts.py @@ -42,8 +42,10 @@ def write_script(name, text): expected.append(name) f = open(os.path.join(source, name), "w") - f.write(text) - f.close() + try: + f.write(text) + finally: + f.close() write_script("script1.py", ("#! /usr/bin/env python2.3\n" "# bogus script w/ Python sh-bang\n" diff --git a/src/distutils2/tests/test_manifest.py b/src/distutils2/tests/test_manifest.py --- a/src/distutils2/tests/test_manifest.py +++ b/src/distutils2/tests/test_manifest.py @@ -44,7 +44,7 @@ # the manifest should have been read # and 3 warnings issued (we ddidn't provided the files) - self.assertEquals(len(warns), 3) + self.assertEqual(len(warns), 3) for warn in warns: self.assertIn('warning: no files found matching', warn) diff --git a/src/distutils2/tests/test_metadata.py b/src/distutils2/tests/test_metadata.py --- a/src/distutils2/tests/test_metadata.py +++ b/src/distutils2/tests/test_metadata.py @@ -5,9 +5,11 @@ from distutils2.metadata import (DistributionMetadata, _interpret, PKG_INFO_PREFERRED_VERSION) -from distutils2.tests.support import unittest +from distutils2.tests.support import unittest, LoggingSilencer +from distutils2.errors import (MetadataConflictError, + MetadataUnrecognizedVersionError) -class DistributionMetadataTestCase(unittest.TestCase): +class DistributionMetadataTestCase(LoggingSilencer, unittest.TestCase): def test_interpret(self): @@ -15,15 +17,15 @@ version = sys.version.split()[0] os_name = os.name - assert _interpret("sys.platform == '%s'" % platform) - assert _interpret("sys.platform == '%s' or python_version == '2.4'" \ - % platform) - assert _interpret("sys.platform == '%s' and " - "python_full_version == '%s'"\ - % (platform, version)) - assert _interpret("'%s' == sys.platform" % platform) + self.assertTrue(_interpret("sys.platform == '%s'" % platform)) + self.assertTrue(_interpret( + "sys.platform == '%s' or python_version == '2.4'" % platform)) + self.assertTrue(_interpret( + "sys.platform == '%s' and python_full_version == '%s'" % + (platform, version))) + self.assertTrue(_interpret("'%s' == sys.platform" % platform)) - assert _interpret('os.name == "%s"' % os_name) + self.assertTrue(_interpret('os.name == "%s"' % os_name)) # stuff that need to raise a syntax error ops = ('os.name == os.name', 'os.name == 2', "'2' == '2'", @@ -35,24 +37,25 @@ OP = 'os.name == "%s"' % os_name AND = ' and ' OR = ' or ' - assert _interpret(OP+AND+OP) - assert _interpret(OP+AND+OP+AND+OP) - assert _interpret(OP+OR+OP) - assert _interpret(OP+OR+OP+OR+OP) + self.assertTrue(_interpret(OP + AND + OP)) + self.assertTrue(_interpret(OP + AND + OP + AND + OP)) + self.assertTrue(_interpret(OP + OR + OP)) + self.assertTrue(_interpret(OP + OR + OP + OR + OP)) # other operators - assert _interpret("os.name != 'buuuu'") - assert _interpret("python_version > '1.0'") - assert _interpret("python_version < '5.0'") - assert _interpret("python_version <= '5.0'") - assert _interpret("python_version >= '1.0'") - assert _interpret("'%s' in os.name" % os_name) - assert _interpret("'buuuu' not in os.name") - assert _interpret("'buuuu' not in os.name and '%s' in os.name" \ - % os_name) + self.assertTrue(_interpret("os.name != 'buuuu'")) + self.assertTrue(_interpret("python_version > '1.0'")) + self.assertTrue(_interpret("python_version < '5.0'")) + self.assertTrue(_interpret("python_version <= '5.0'")) + self.assertTrue(_interpret("python_version >= '1.0'")) + self.assertTrue(_interpret("'%s' in os.name" % os_name)) + self.assertTrue(_interpret("'buuuu' not in os.name")) + self.assertTrue(_interpret( + "'buuuu' not in os.name and '%s' in os.name" % os_name)) # execution context - assert _interpret('python_version == "0.1"', {'python_version': '0.1'}) + self.assertTrue(_interpret('python_version == "0.1"', + {'python_version': '0.1'})) def test_metadata_read_write(self): @@ -63,25 +66,28 @@ res.seek(0) res = res.read() f = open(PKG_INFO) - wanted = f.read() + try: + # XXX this is not used + wanted = f.read() + finally: + f.close() self.assertTrue('Keywords: keyring,password,crypt' in res) - f.close() def test_metadata_markers(self): # see if we can be platform-aware PKG_INFO = os.path.join(os.path.dirname(__file__), 'PKG-INFO') content = open(PKG_INFO).read() content = content % sys.platform - metadata = DistributionMetadata(platform_dependant=True) + metadata = DistributionMetadata(platform_dependent=True) metadata.read_file(StringIO(content)) - self.assertEquals(metadata['Requires-Dist'], ['bar']) + self.assertEqual(metadata['Requires-Dist'], ['bar']) # test with context context = {'sys.platform': 'okook'} - metadata = DistributionMetadata(platform_dependant=True, + metadata = DistributionMetadata(platform_dependent=True, execution_context=context) metadata.read_file(StringIO(content)) - self.assertEquals(metadata['Requires-Dist'], ['foo']) + self.assertEqual(metadata['Requires-Dist'], ['foo']) def test_description(self): PKG_INFO = os.path.join(os.path.dirname(__file__), 'PKG-INFO') @@ -93,14 +99,14 @@ # see if we can read the description now DESC = os.path.join(os.path.dirname(__file__), 'LONG_DESC.txt') wanted = open(DESC).read() - self.assertEquals(wanted, metadata['Description']) + self.assertEqual(wanted, metadata['Description']) # save the file somewhere and make sure we can read it back out = StringIO() metadata.write_file(out) out.seek(0) metadata.read_file(out) - self.assertEquals(wanted, metadata['Description']) + self.assertEqual(wanted, metadata['Description']) def test_mapper_apis(self): PKG_INFO = os.path.join(os.path.dirname(__file__), 'PKG-INFO') @@ -115,25 +121,32 @@ def test_versions(self): metadata = DistributionMetadata() metadata['Obsoletes'] = 'ok' - self.assertEquals(metadata['Metadata-Version'], '1.1') + self.assertEqual(metadata['Metadata-Version'], '1.1') del metadata['Obsoletes'] metadata['Obsoletes-Dist'] = 'ok' - self.assertEquals(metadata['Metadata-Version'], '1.2') + self.assertEqual(metadata['Metadata-Version'], '1.2') + self.assertRaises(MetadataConflictError, metadata.set, + 'Obsoletes', 'ok') + + del metadata['Obsoletes'] del metadata['Obsoletes-Dist'] metadata['Version'] = '1' - self.assertEquals(metadata['Metadata-Version'], '1.0') + self.assertEqual(metadata['Metadata-Version'], '1.0') PKG_INFO = os.path.join(os.path.dirname(__file__), 'SETUPTOOLS-PKG-INFO') metadata.read_file(StringIO(open(PKG_INFO).read())) - self.assertEquals(metadata['Metadata-Version'], '1.0') + self.assertEqual(metadata['Metadata-Version'], '1.0') PKG_INFO = os.path.join(os.path.dirname(__file__), 'SETUPTOOLS-PKG-INFO2') metadata.read_file(StringIO(open(PKG_INFO).read())) - self.assertEquals(metadata['Metadata-Version'], '1.1') + self.assertEqual(metadata['Metadata-Version'], '1.1') + + metadata.version = '1.618' + self.assertRaises(MetadataUnrecognizedVersionError, metadata.keys) def test_warnings(self): metadata = DistributionMetadata() @@ -161,7 +174,7 @@ # we should have a certain amount of warnings num_wanted = len(values) - self.assertEquals(num_wanted, res) + self.assertEqual(num_wanted, res) def test_multiple_predicates(self): metadata = DistributionMetadata() @@ -184,29 +197,29 @@ res = m.warns del m.warns - self.assertEquals(res, 0) + self.assertEqual(res, 0) def test_project_url(self): metadata = DistributionMetadata() metadata['Project-URL'] = [('one', 'http://ok')] - self.assertEquals(metadata['Project-URL'], + self.assertEqual(metadata['Project-URL'], [('one', 'http://ok')]) - self.assertEquals(metadata.version, '1.2') + self.assertEqual(metadata.version, '1.2') def test_check(self): metadata = DistributionMetadata() metadata['Version'] = 'rr' metadata['Requires-dist'] = ['Foo (a)'] missing, warnings = metadata.check() - self.assertEquals(missing, ['Name', 'Home-page']) - self.assertEquals(len(warnings), 2) + self.assertEqual(missing, ['Name', 'Home-page']) + self.assertEqual(len(warnings), 2) def test_best_choice(self): metadata = DistributionMetadata() metadata['Version'] = '1.0' - self.assertEquals(metadata.version, PKG_INFO_PREFERRED_VERSION) + self.assertEqual(metadata.version, PKG_INFO_PREFERRED_VERSION) metadata['Classifier'] = ['ok'] - self.assertEquals(metadata.version, '1.2') + self.assertEqual(metadata.version, '1.2') def test_project_urls(self): # project-url is a bit specific, make sure we write it @@ -214,7 +227,7 @@ metadata = DistributionMetadata() metadata['Version'] = '1.0' metadata['Project-Url'] = [('one', 'http://ok')] - self.assertEquals(metadata['Project-Url'], [('one', 'http://ok')]) + self.assertEqual(metadata['Project-Url'], [('one', 'http://ok')]) file_ = StringIO() metadata.write_file(file_) file_.seek(0) @@ -224,7 +237,7 @@ file_.seek(0) metadata = DistributionMetadata() metadata.read_file(file_) - self.assertEquals(metadata['Project-Url'], [('one', 'http://ok')]) + self.assertEqual(metadata['Project-Url'], [('one', 'http://ok')]) def test_suite(): diff --git a/src/distutils2/tests/test_msvc9compiler.py b/src/distutils2/tests/test_msvc9compiler.py --- a/src/distutils2/tests/test_msvc9compiler.py +++ b/src/distutils2/tests/test_msvc9compiler.py @@ -105,7 +105,7 @@ import _winreg HKCU = _winreg.HKEY_CURRENT_USER keys = Reg.read_keys(HKCU, 'xxxx') - self.assertEquals(keys, None) + self.assertEqual(keys, None) keys = Reg.read_keys(HKCU, r'Control Panel') self.assertTrue('Desktop' in keys) @@ -116,20 +116,24 @@ tempdir = self.mkdtemp() manifest = os.path.join(tempdir, 'manifest') f = open(manifest, 'w') - f.write(_MANIFEST) - f.close() + try: + f.write(_MANIFEST) + finally: + f.close() compiler = MSVCCompiler() compiler._remove_visual_c_ref(manifest) # see what we got f = open(manifest) - # removing trailing spaces - content = '\n'.join([line.rstrip() for line in f.readlines()]) - f.close() + try: + # removing trailing spaces + content = '\n'.join([line.rstrip() for line in f.readlines()]) + finally: + f.close() # makes sure the manifest was properly cleaned - self.assertEquals(content, _CLEANED_MANIFEST) + self.assertEqual(content, _CLEANED_MANIFEST) def test_suite(): diff --git a/src/distutils2/tests/test_pypi_dist.py b/src/distutils2/tests/test_pypi_dist.py new file mode 100644 --- /dev/null +++ b/src/distutils2/tests/test_pypi_dist.py @@ -0,0 +1,247 @@ +"""Tests for the distutils2.pypi.dist module.""" + +import os +import shutil +import tempfile + +from distutils2.tests.pypi_server import use_pypi_server +from distutils2.tests import run_unittest +from distutils2.tests.support import unittest, TempdirManager +from distutils2.version import VersionPredicate +from distutils2.pypi.errors import HashDoesNotMatch, UnsupportedHashName +from distutils2.pypi.dist import (PyPIDistribution as Dist, + PyPIDistributions as Dists, + split_archive_name) + + +class TestPyPIDistribution(TempdirManager, + unittest.TestCase): + """Tests the pypi.dist.PyPIDistribution class""" + + def test_instanciation(self): + # Test the Distribution class provides us the good attributes when + # given on construction + dist = Dist("FooBar", "1.1") + self.assertEqual("FooBar", dist.name) + self.assertEqual("1.1", "%s" % dist.version) + + def test_create_from_url(self): + # Test that the Distribution object can be built from a single URL + url_list = { + 'FooBar-1.1.0.tar.gz': { + 'name': 'foobar', # lowercase the name + 'version': '1.1', + }, + 'Foo-Bar-1.1.0.zip': { + 'name': 'foo-bar', # keep the dash + 'version': '1.1', + }, + 'foobar-1.1b2.tar.gz#md5=123123123123123': { + 'name': 'foobar', + 'version': '1.1b2', + 'url': { + 'url': 'http://test.tld/foobar-1.1b2.tar.gz', # no hash + 'hashval': '123123123123123', + 'hashname': 'md5', + } + }, + 'foobar-1.1-rc2.tar.gz': { # use suggested name + 'name': 'foobar', + 'version': '1.1c2', + 'url': { + 'url': 'http://test.tld/foobar-1.1-rc2.tar.gz', + } + } + } + + for url, attributes in url_list.items(): + dist = Dist.from_url("http://test.tld/" + url) + for attribute, value in attributes.items(): + if isinstance(value, dict): + mylist = getattr(dist, attribute) + for val in value.keys(): + self.assertEqual(value[val], mylist[val]) + else: + if attribute == "version": + self.assertEqual("%s" % getattr(dist, "version"), value) + else: + self.assertEqual(getattr(dist, attribute), value) + + def test_get_url(self): + # Test that the url property works well + + d = Dist("foobar", "1.1", url="test_url") + self.assertDictEqual(d.url, { + "url": "test_url", + "is_external": True, + "hashname": None, + "hashval": None, + }) + + # add a new url + d.add_url(url="internal_url", is_external=False) + self.assertEqual(d._url, None) + self.assertDictEqual(d.url, { + "url": "internal_url", + "is_external": False, + "hashname": None, + "hashval": None, + }) + self.assertEqual(2, len(d._urls)) + + def test_comparaison(self): + # Test that we can compare PyPIDistributions + foo1 = Dist("foo", "1.0") + foo2 = Dist("foo", "2.0") + bar = Dist("bar", "2.0") + # assert we use the version to compare + self.assertTrue(foo1 < foo2) + self.assertFalse(foo1 > foo2) + self.assertFalse(foo1 == foo2) + + # assert we can't compare dists with different names + self.assertRaises(TypeError, foo1.__eq__, bar) + + def test_split_archive_name(self): + # Test we can split the archive names + names = { + 'foo-bar-baz-1.0-rc2': ('foo-bar-baz', '1.0c2'), + 'foo-bar-baz-1.0': ('foo-bar-baz', '1.0'), + 'foobarbaz-1.0': ('foobarbaz', '1.0'), + } + for name, results in names.items(): + self.assertEqual(results, split_archive_name(name)) + + @use_pypi_server("downloads_with_md5") + def test_download(self, server): + # Download is possible, and the md5 is checked if given + + add_to_tmpdirs = lambda x: self.tempdirs.append(os.path.dirname(x)) + + url = "%s/simple/foobar/foobar-0.1.tar.gz" % server.full_address + # check md5 if given + dist = Dist("FooBar", "0.1", url=url, + url_hashname="md5", url_hashval="d41d8cd98f00b204e9800998ecf8427e") + add_to_tmpdirs(dist.download()) + + # a wrong md5 fails + dist2 = Dist("FooBar", "0.1", url=url, + url_hashname="md5", url_hashval="wrongmd5") + + self.assertRaises(HashDoesNotMatch, dist2.download) + add_to_tmpdirs(dist2.downloaded_location) + + # we can omit the md5 hash + dist3 = Dist("FooBar", "0.1", url=url) + add_to_tmpdirs(dist3.download()) + + # and specify a temporary location + # for an already downloaded dist + path1 = self.mkdtemp() + dist3.download(path=path1) + # and for a new one + path2_base = self.mkdtemp() + dist4 = Dist("FooBar", "0.1", url=url) + path2 = dist4.download(path=path2_base) + self.assertTrue(path2_base in path2) + + def test_hashname(self): + # Invalid hashnames raises an exception on assignation + Dist("FooBar", "0.1", url_hashname="md5", url_hashval="value") + + self.assertRaises(UnsupportedHashName, Dist, "FooBar", "0.1", + url_hashname="invalid_hashname", url_hashval="value") + + +class TestPyPIDistributions(unittest.TestCase): + + def test_filter(self): + # Test we filter the distributions the right way, using version + # predicate match method + dists = Dists(( + Dist("FooBar", "1.1"), + Dist("FooBar", "1.1.1"), + Dist("FooBar", "1.2"), + Dist("FooBar", "1.2.1"), + )) + filtered = dists.filter(VersionPredicate("FooBar (<1.2)")) + self.assertNotIn(dists[2], filtered) + self.assertNotIn(dists[3], filtered) + self.assertIn(dists[0], filtered) + self.assertIn(dists[1], filtered) + + def test_append(self): + # When adding a new item to the list, the behavior is to test if + # a distribution with the same name and version number already exists, + # and if so, to add url informations to the existing PyPIDistribution + # object. + # If no object matches, just add "normally" the object to the list. + + dists = Dists([ + Dist("FooBar", "1.1", url="external_url", type="source"), + ]) + self.assertEqual(1, len(dists)) + dists.append(Dist("FooBar", "1.1", url="internal_url", + url_is_external=False, type="source")) + self.assertEqual(1, len(dists)) + self.assertEqual(2, len(dists[0]._urls)) + + dists.append(Dist("Foobar", "1.1.1", type="source")) + self.assertEqual(2, len(dists)) + + # when adding a distribution whith a different type, a new distribution + # has to be added. + dists.append(Dist("Foobar", "1.1.1", type="binary")) + self.assertEqual(3, len(dists)) + + def test_prefer_final(self): + # Can order the distributions using prefer_final + + fb10 = Dist("FooBar", "1.0") # final distribution + fb11a = Dist("FooBar", "1.1a1") # alpha + fb12a = Dist("FooBar", "1.2a1") # alpha + fb12b = Dist("FooBar", "1.2b1") # beta + dists = Dists([fb10, fb11a, fb12a, fb12b]) + + dists.sort_distributions(prefer_final=True) + self.assertEqual(fb10, dists[0]) + + dists.sort_distributions(prefer_final=False) + self.assertEqual(fb12b, dists[0]) + + def test_prefer_source(self): + # Ordering support prefer_source + fb_source = Dist("FooBar", "1.0", type="source") + fb_binary = Dist("FooBar", "1.0", type="binary") + fb2_binary = Dist("FooBar", "2.0", type="binary") + dists = Dists([fb_binary, fb_source]) + + dists.sort_distributions(prefer_source=True) + self.assertEqual(fb_source, dists[0]) + + dists.sort_distributions(prefer_source=False) + self.assertEqual(fb_binary, dists[0]) + + dists.append(fb2_binary) + dists.sort_distributions(prefer_source=True) + self.assertEqual(fb2_binary, dists[0]) + + def test_get_same_name_and_version(self): + # PyPIDistributions can return a list of "duplicates" + fb_source = Dist("FooBar", "1.0", type="source") + fb_binary = Dist("FooBar", "1.0", type="binary") + fb2_binary = Dist("FooBar", "2.0", type="binary") + dists = Dists([fb_binary, fb_source, fb2_binary]) + duplicates = dists.get_same_name_and_version() + self.assertTrue(1, len(duplicates)) + self.assertIn(fb_source, duplicates[0]) + + +def test_suite(): + suite = unittest.TestSuite() + suite.addTest(unittest.makeSuite(TestPyPIDistribution)) + suite.addTest(unittest.makeSuite(TestPyPIDistributions)) + return suite + +if __name__ == '__main__': + run_unittest(test_suite()) diff --git a/src/distutils2/tests/test_pypi_server.py b/src/distutils2/tests/test_pypi_server.py new file mode 100644 --- /dev/null +++ b/src/distutils2/tests/test_pypi_server.py @@ -0,0 +1,69 @@ +"""Tests for distutils.command.bdist.""" +import urllib +import urllib2 +import os.path + +from distutils2.tests.pypi_server import PyPIServer, PYPI_DEFAULT_STATIC_PATH +from distutils2.tests.support import unittest + + +class PyPIServerTest(unittest.TestCase): + + def test_records_requests(self): + # We expect that PyPIServer can log our requests + server = PyPIServer() + server.start() + self.assertEqual(len(server.requests), 0) + + data = "Rock Around The Bunker" + headers = {"X-test-header": "Mister Iceberg"} + + request = urllib2.Request(server.full_address, data, headers) + urllib2.urlopen(request) + self.assertEqual(len(server.requests), 1) + handler, request_data = server.requests[-1] + self.assertIn("Rock Around The Bunker", request_data) + self.assertIn("x-test-header", handler.headers.dict) + self.assertEqual(handler.headers.dict["x-test-header"], + "Mister Iceberg") + server.stop() + + def test_serve_static_content(self): + # PYPI Mocked server can serve static content from disk. + + def uses_local_files_for(server, url_path): + """Test that files are served statically (eg. the output from the + server is the same than the one made by a simple file read. + """ + url = server.full_address + url_path + request = urllib2.Request(url) + response = urllib2.urlopen(request) + file = open(PYPI_DEFAULT_STATIC_PATH + "/test_pypi_server" + + url_path) + return response.read() == file.read() + + server = PyPIServer(static_uri_paths=["simple", "external"], + static_filesystem_paths=["test_pypi_server"]) + server.start() + + # the file does not exists on the disc, so it might not be served + url = server.full_address + "/simple/unexisting_page" + request = urllib2.Request(url) + try: + urllib2.urlopen(request) + except urllib2.HTTPError,e: + self.assertEqual(e.code, 404) + + # now try serving a content that do exists + self.assertTrue(uses_local_files_for(server, "/simple/index.html")) + + # and another one in another root path + self.assertTrue(uses_local_files_for(server, "/external/index.html")) + server.stop() + + +def test_suite(): + return unittest.makeSuite(PyPIServerTest) + +if __name__ == '__main__': + unittest.main(defaultTest="test_suite") diff --git a/src/distutils2/tests/test_pypi_simple.py b/src/distutils2/tests/test_pypi_simple.py new file mode 100644 --- /dev/null +++ b/src/distutils2/tests/test_pypi_simple.py @@ -0,0 +1,277 @@ +"""Tests for the pypi.simple module. + +""" +import sys +import os +import shutil +import tempfile +import urllib2 + +from distutils2.pypi import simple +from distutils2.tests import support, run_unittest +from distutils2.tests.support import unittest +from distutils2.tests.pypi_server import (use_pypi_server, PyPIServer, + PYPI_DEFAULT_STATIC_PATH) + + +class PyPISimpleTestCase(support.TempdirManager, + unittest.TestCase): + + def _get_simple_index(self, server, base_url="/simple/", hosts=None, + *args, **kwargs): + """Build and return a SimpleSimpleIndex instance, with the test server + urls + """ + if hosts is None: + hosts = (server.full_address.strip("http://"),) + kwargs['hosts'] = hosts + return simple.SimpleIndex(server.full_address + base_url, *args, + **kwargs) + + def test_bad_urls(self): + index = simple.SimpleIndex() + url = 'http://127.0.0.1:0/nonesuch/test_simple' + try: + v = index._open_url(url) + except Exception, v: + self.assertTrue(url in str(v)) + else: + self.assertTrue(isinstance(v, urllib2.HTTPError)) + + # issue 16 + # easy_install inquant.contentmirror.plone breaks because of a typo + # in its home URL + index = simple.SimpleIndex(hosts=('www.example.com',)) + url = 'url:%20https://svn.plone.org/svn/collective/inquant.contentmirror.plone/trunk' + try: + v = index._open_url(url) + except Exception, v: + self.assertTrue(url in str(v)) + else: + self.assertTrue(isinstance(v, urllib2.HTTPError)) + + def _urlopen(*args): + import httplib + raise httplib.BadStatusLine('line') + + old_urlopen = urllib2.urlopen + urllib2.urlopen = _urlopen + url = 'http://example.com' + try: + try: + v = index._open_url(url) + except Exception, v: + self.assertTrue('line' in str(v)) + else: + raise AssertionError('Should have raise here!') + finally: + urllib2.urlopen = old_urlopen + + # issue 20 + url = 'http://http://svn.pythonpaste.org/Paste/wphp/trunk' + try: + index._open_url(url) + except Exception, v: + self.assertTrue('nonnumeric port' in str(v)) + + # issue #160 + if sys.version_info[0] == 2 and sys.version_info[1] == 7: + # this should not fail + url = 'http://example.com' + page = ('') + index._process_url(url, page) + + @use_pypi_server("test_found_links") + def test_found_links(self, server): + # Browse the index, asking for a specified distribution version + # The PyPI index contains links for version 1.0, 1.1, 2.0 and 2.0.1 + index = self._get_simple_index(server) + last_distribution = index.get("foobar") + + # we have scanned the index page + self.assertIn(server.full_address + "/simple/foobar/", + index._processed_urls) + + # we have found 4 distributions in this page + self.assertEqual(len(index._distributions["foobar"]), 4) + + # and returned the most recent one + self.assertEqual("%s" % last_distribution.version, '2.0.1') + + def test_is_browsable(self): + index = simple.SimpleIndex(follow_externals=False) + self.assertTrue(index._is_browsable(index.index_url + "test")) + + # Now, when following externals, we can have a list of hosts to trust. + # and don't follow other external links than the one described here. + index = simple.SimpleIndex(hosts=["pypi.python.org", "test.org"], + follow_externals=True) + good_urls = ( + "http://pypi.python.org/foo/bar", + "http://pypi.python.org/simple/foobar", + "http://test.org", + "http://test.org/", + "http://test.org/simple/", + ) + bad_urls = ( + "http://python.org", + "http://test.tld", + ) + + for url in good_urls: + self.assertTrue(index._is_browsable(url)) + + for url in bad_urls: + self.assertFalse(index._is_browsable(url)) + + # allow all hosts + index = simple.SimpleIndex(follow_externals=True, hosts=("*",)) + self.assertTrue(index._is_browsable("http://an-external.link/path")) + self.assertTrue(index._is_browsable("pypi.test.tld/a/path")) + + # specify a list of hosts we want to allow + index = simple.SimpleIndex(follow_externals=True, + hosts=("*.test.tld",)) + self.assertFalse(index._is_browsable("http://an-external.link/path")) + self.assertTrue(index._is_browsable("http://pypi.test.tld/a/path")) + + @use_pypi_server("with_externals") + def test_restrict_hosts(self, server): + # Include external pages + # Try to request the package index, wich contains links to "externals" + # resources. They have to be scanned too. + index = self._get_simple_index(server, follow_externals=True) + index.get("foobar") + self.assertIn(server.full_address + "/external/external.html", + index._processed_urls) + + @use_pypi_server("with_real_externals") + def test_restrict_hosts(self, server): + # Only use a list of allowed hosts is possible + # Test that telling the simple pyPI client to not retrieve external + # works + index = self._get_simple_index(server, follow_externals=False) + index.get("foobar") + self.assertNotIn(server.full_address + "/external/external.html", + index._processed_urls) + + @use_pypi_server(static_filesystem_paths=["with_externals"], + static_uri_paths=["simple", "external"]) + def test_links_priority(self, server): + # Download links from the pypi simple index should be used before + # external download links. + # http://bitbucket.org/tarek/distribute/issue/163/md5-validation-error + # + # Usecase : + # - someone uploads a package on pypi, a md5 is generated + # - someone manually coindexes this link (with the md5 in the url) onto + # an external page accessible from the package page. + # - someone reuploads the package (with a different md5) + # - while easy_installing, an MD5 error occurs because the external link + # is used + # -> The index should use the link from pypi, not the external one. + + # start an index server + index_url = server.full_address + '/simple/' + + # scan a test index + index = simple.SimpleIndex(index_url, follow_externals=True) + dists = index.find("foobar") + server.stop() + + # we have only one link, because links are compared without md5 + self.assertEqual(len(dists), 1) + # the link should be from the index + self.assertEqual('12345678901234567', dists[0].url['hashval']) + self.assertEqual('md5', dists[0].url['hashname']) + + @use_pypi_server(static_filesystem_paths=["with_norel_links"], + static_uri_paths=["simple", "external"]) + def test_not_scan_all_links(self, server): + # Do not follow all index page links. + # The links not tagged with rel="download" and rel="homepage" have + # to not be processed by the package index, while processing "pages". + + # process the pages + index = self._get_simple_index(server, follow_externals=True) + index.find("foobar") + # now it should have processed only pages with links rel="download" + # and rel="homepage" + self.assertIn("%s/simple/foobar/" % server.full_address, + index._processed_urls) # it's the simple index page + self.assertIn("%s/external/homepage.html" % server.full_address, + index._processed_urls) # the external homepage is rel="homepage" + self.assertNotIn("%s/external/nonrel.html" % server.full_address, + index._processed_urls) # this link contains no rel=* + self.assertNotIn("%s/unrelated-0.2.tar.gz" % server.full_address, + index._processed_urls) # linked from simple index (no rel) + self.assertIn("%s/foobar-0.1.tar.gz" % server.full_address, + index._processed_urls) # linked from simple index (rel) + self.assertIn("%s/foobar-2.0.tar.gz" % server.full_address, + index._processed_urls) # linked from external homepage (rel) + + def test_uses_mirrors(self): + # When the main repository seems down, try using the given mirrors""" + server = PyPIServer("foo_bar_baz") + mirror = PyPIServer("foo_bar_baz") + mirror.start() # we dont start the server here + + try: + # create the index using both servers + index = simple.SimpleIndex(server.full_address + "/simple/", + hosts=('*',), timeout=1, # set the timeout to 1s for the tests + mirrors=[mirror.full_address + "/simple/",]) + + # this should not raise a timeout + self.assertEqual(4, len(index.find("foo"))) + finally: + mirror.stop() + + def test_simple_link_matcher(self): + # Test that the simple link matcher yields the right links""" + index = simple.SimpleIndex(follow_externals=False) + + # Here, we define: + # 1. one link that must be followed, cause it's a download one + # 2. one link that must *not* be followed, cause the is_browsable + # returns false for it. + # 3. one link that must be followed cause it's a homepage that is + # browsable + self.assertTrue(index._is_browsable("%stest" % index.index_url)) + self.assertFalse(index._is_browsable("http://dl-link2")) + content = """ + download_link1 + homepage_link1 + homepage_link2 + """ % index.index_url + + # Test that the simple link matcher yield the good links. + generator = index._simple_link_matcher(content, index.index_url) + self.assertEqual(('http://dl-link1', True), generator.next()) + self.assertEqual(('%stest' % index.index_url, False), + generator.next()) + self.assertRaises(StopIteration, generator.next) + + # Follow the external links is possible + index.follow_externals = True + generator = index._simple_link_matcher(content, index.index_url) + self.assertEqual(('http://dl-link1', True), generator.next()) + self.assertEqual(('http://dl-link2', False), generator.next()) + self.assertEqual(('%stest' % index.index_url, False), + generator.next()) + self.assertRaises(StopIteration, generator.next) + + def test_browse_local_files(self): + # Test that we can browse local files""" + index_path = os.sep.join(["file://" + PYPI_DEFAULT_STATIC_PATH, + "test_found_links", "simple"]) + index = simple.SimpleIndex(index_path) + dists = index.find("foobar") + self.assertEqual(4, len(dists)) + +def test_suite(): + return unittest.makeSuite(PyPISimpleTestCase) + +if __name__ == '__main__': + unittest.main(defaultTest="test_suite") diff --git a/src/distutils2/tests/test_register.py b/src/distutils2/tests/test_register.py --- a/src/distutils2/tests/test_register.py +++ b/src/distutils2/tests/test_register.py @@ -124,7 +124,7 @@ # with the content similar to WANTED_PYPIRC content = open(self.rc).read() - self.assertEquals(content, WANTED_PYPIRC) + self.assertEqual(content, WANTED_PYPIRC) # now let's make sure the .pypirc file generated # really works : we shouldn't be asked anything @@ -141,7 +141,7 @@ self.assertTrue(self.conn.reqs, 2) req1 = dict(self.conn.reqs[0].headers) req2 = dict(self.conn.reqs[1].headers) - self.assertEquals(req2['Content-length'], req1['Content-length']) + self.assertEqual(req2['Content-length'], req1['Content-length']) self.assertTrue('xxx' in self.conn.reqs[1].data) def test_password_not_in_file(self): @@ -154,7 +154,7 @@ # dist.password should be set # therefore used afterwards by other commands - self.assertEquals(cmd.distribution.password, 'password') + self.assertEqual(cmd.distribution.password, 'password') def test_registering(self): # this test runs choice 2 @@ -171,7 +171,7 @@ self.assertTrue(self.conn.reqs, 1) req = self.conn.reqs[0] headers = dict(req.headers) - self.assertEquals(headers['Content-length'], '608') + self.assertEqual(headers['Content-length'], '608') self.assertTrue('tarek' in req.data) def test_password_reset(self): @@ -189,7 +189,7 @@ self.assertTrue(self.conn.reqs, 1) req = self.conn.reqs[0] headers = dict(req.headers) - self.assertEquals(headers['Content-length'], '290') + self.assertEqual(headers['Content-length'], '290') self.assertTrue('tarek' in req.data) @unittest.skipUnless(DOCUTILS_SUPPORT, 'needs docutils') @@ -246,8 +246,8 @@ cmd.ensure_finalized() cmd.distribution.metadata['Requires-Dist'] = ['lxml'] data = cmd.build_post_data('submit') - self.assertEquals(data['metadata_version'], '1.2') - self.assertEquals(data['requires_dist'], ['lxml']) + self.assertEqual(data['metadata_version'], '1.2') + self.assertEqual(data['requires_dist'], ['lxml']) def test_suite(): return unittest.makeSuite(RegisterTestCase) diff --git a/src/distutils2/tests/test_sdist.py b/src/distutils2/tests/test_sdist.py --- a/src/distutils2/tests/test_sdist.py +++ b/src/distutils2/tests/test_sdist.py @@ -20,7 +20,6 @@ from os.path import join import sys -import tempfile import warnings from distutils2.tests import captured_stdout @@ -128,7 +127,7 @@ # now let's check what we have dist_folder = join(self.tmp_dir, 'dist') files = os.listdir(dist_folder) - self.assertEquals(files, ['fake-1.0.zip']) + self.assertEqual(files, ['fake-1.0.zip']) zip_file = zipfile.ZipFile(join(dist_folder, 'fake-1.0.zip')) try: @@ -137,7 +136,7 @@ zip_file.close() # making sure everything has been pruned correctly - self.assertEquals(len(content), 4) + self.assertEqual(len(content), 4) @unittest.skipUnless(zlib, "requires zlib") def test_make_distribution(self): @@ -159,7 +158,7 @@ dist_folder = join(self.tmp_dir, 'dist') result = os.listdir(dist_folder) result.sort() - self.assertEquals(result, + self.assertEqual(result, ['fake-1.0.tar', 'fake-1.0.tar.gz'] ) os.remove(join(dist_folder, 'fake-1.0.tar')) @@ -173,7 +172,7 @@ result = os.listdir(dist_folder) result.sort() - self.assertEquals(result, + self.assertEqual(result, ['fake-1.0.tar', 'fake-1.0.tar.gz']) @unittest.skipUnless(zlib, "requires zlib") @@ -223,7 +222,7 @@ # now let's check what we have dist_folder = join(self.tmp_dir, 'dist') files = os.listdir(dist_folder) - self.assertEquals(files, ['fake-1.0.zip']) + self.assertEqual(files, ['fake-1.0.zip']) zip_file = zipfile.ZipFile(join(dist_folder, 'fake-1.0.zip')) try: @@ -232,11 +231,11 @@ zip_file.close() # making sure everything was added - self.assertEquals(len(content), 11) + self.assertEqual(len(content), 11) # checking the MANIFEST manifest = open(join(self.tmp_dir, 'MANIFEST')).read() - self.assertEquals(manifest, MANIFEST % {'sep': os.sep}) + self.assertEqual(manifest, MANIFEST % {'sep': os.sep}) @unittest.skipUnless(zlib, "requires zlib") def test_metadata_check_option(self): @@ -248,7 +247,7 @@ cmd.ensure_finalized() cmd.run() warnings = self.get_logs(WARN) - self.assertEquals(len(warnings), 1) + self.assertEqual(len(warnings), 1) # trying with a complete set of metadata self.clear_logs() @@ -260,7 +259,7 @@ # removing manifest generated warnings warnings = [warn for warn in warnings if not warn.endswith('-- skipping')] - self.assertEquals(len(warnings), 0) + self.assertEqual(len(warnings), 0) def test_show_formats(self): @@ -270,7 +269,7 @@ num_formats = len(get_archive_formats()) output = [line for line in stdout.split('\n') if line.strip().startswith('--formats=')] - self.assertEquals(len(output), num_formats) + self.assertEqual(len(output), num_formats) def test_finalize_options(self): @@ -278,9 +277,9 @@ cmd.finalize_options() # default options set by finalize - self.assertEquals(cmd.manifest, 'MANIFEST') - self.assertEquals(cmd.template, 'MANIFEST.in') - self.assertEquals(cmd.dist_dir, 'dist') + self.assertEqual(cmd.manifest, 'MANIFEST') + self.assertEqual(cmd.template, 'MANIFEST.in') + self.assertEqual(cmd.dist_dir, 'dist') # formats has to be a string splitable on (' ', ',') or # a stringlist @@ -317,8 +316,8 @@ archive = tarfile.open(archive_name) try: for member in archive.getmembers(): - self.assertEquals(member.uid, 0) - self.assertEquals(member.gid, 0) + self.assertEqual(member.uid, 0) + self.assertEqual(member.gid, 0) finally: archive.close() @@ -339,7 +338,7 @@ # rights (see #7408) try: for member in archive.getmembers(): - self.assertEquals(member.uid, os.getuid()) + self.assertEqual(member.uid, os.getuid()) finally: archive.close() diff --git a/src/distutils2/tests/test_spawn.py b/src/distutils2/tests/test_spawn.py --- a/src/distutils2/tests/test_spawn.py +++ b/src/distutils2/tests/test_spawn.py @@ -20,7 +20,7 @@ (['nochange', 'nospace'], ['nochange', 'nospace'])): res = _nt_quote_args(args) - self.assertEquals(res, wanted) + self.assertEqual(res, wanted) @unittest.skipUnless(os.name in ('nt', 'posix'), diff --git a/src/distutils2/tests/test_upload.py b/src/distutils2/tests/test_upload.py --- a/src/distutils2/tests/test_upload.py +++ b/src/distutils2/tests/test_upload.py @@ -1,34 +1,16 @@ """Tests for distutils.command.upload.""" # -*- encoding: utf8 -*- +import os import sys -import os -from distutils2.command import upload as upload_mod from distutils2.command.upload import upload from distutils2.core import Distribution from distutils2.tests import support +from distutils2.tests.pypi_server import PyPIServer, PyPIServerTestCase from distutils2.tests.support import unittest from distutils2.tests.test_config import PYPIRC, PyPIRCCommandTestCase -PYPIRC_LONG_PASSWORD = """\ -[distutils] - -index-servers = - server1 - server2 - -[server1] -username:me -password:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa - -[server2] -username:meagain -password: secret -realm:acme -repository:http://another.pypi/ -""" - PYPIRC_NOPASSWORD = """\ [distutils] @@ -40,47 +22,18 @@ username:me """ -class FakeOpen(object): - - def __init__(self, url): - self.url = url - if not isinstance(url, str): - self.req = url - else: - self.req = None - self.msg = 'OK' - - def getcode(self): - return 200 - - -class uploadTestCase(PyPIRCCommandTestCase): - - def setUp(self): - super(uploadTestCase, self).setUp() - self.old_open = upload_mod.urlopen - upload_mod.urlopen = self._urlopen - self.last_open = None - - def tearDown(self): - upload_mod.urlopen = self.old_open - super(uploadTestCase, self).tearDown() - - def _urlopen(self, url): - self.last_open = FakeOpen(url) - return self.last_open +class UploadTestCase(PyPIServerTestCase, PyPIRCCommandTestCase): def test_finalize_options(self): - # new format self.write_file(self.rc, PYPIRC) dist = Distribution() cmd = upload(dist) cmd.finalize_options() - for attr, waited in (('username', 'me'), ('password', 'secret'), - ('realm', 'pypi'), - ('repository', 'http://pypi.python.org/pypi')): - self.assertEquals(getattr(cmd, attr), waited) + for attr, expected in (('username', 'me'), ('password', 'secret'), + ('realm', 'pypi'), + ('repository', 'http://pypi.python.org/pypi')): + self.assertEqual(getattr(cmd, attr), expected) def test_saved_password(self): # file with no password @@ -89,44 +42,42 @@ # make sure it passes dist = Distribution() cmd = upload(dist) - cmd.finalize_options() - self.assertEquals(cmd.password, None) + cmd.ensure_finalized() + self.assertEqual(cmd.password, None) # make sure we get it as well, if another command # initialized it at the dist level dist.password = 'xxx' cmd = upload(dist) cmd.finalize_options() - self.assertEquals(cmd.password, 'xxx') + self.assertEqual(cmd.password, 'xxx') def test_upload(self): - tmp = self.mkdtemp() - path = os.path.join(tmp, 'xxx') + path = os.path.join(self.tmp_dir, 'xxx') self.write_file(path) command, pyversion, filename = 'xxx', '2.6', path dist_files = [(command, pyversion, filename)] - self.write_file(self.rc, PYPIRC_LONG_PASSWORD) # lets run it pkg_dir, dist = self.create_dist(dist_files=dist_files, author=u'd??d??') cmd = upload(dist) cmd.ensure_finalized() + cmd.repository = self.pypi.full_address cmd.run() # what did we send ? - self.assertIn('d??d??', self.last_open.req.data) - headers = dict(self.last_open.req.headers) - self.assertTrue(int(headers['Content-length']) < 2000) - self.assertTrue(headers['Content-type'].startswith('multipart/form-data')) - self.assertEquals(self.last_open.req.get_method(), 'POST') - self.assertEquals(self.last_open.req.get_full_url(), - 'http://pypi.python.org/pypi') - self.assertTrue('xxx' in self.last_open.req.data) - auth = self.last_open.req.headers['Authorization'] - self.assertFalse('\n' in auth) + handler, request_data = self.pypi.requests[-1] + headers = handler.headers.dict + self.assertIn('d??d??', request_data) + self.assertIn('xxx', request_data) + self.assertEqual(int(headers['content-length']), len(request_data)) + self.assertTrue(int(headers['content-length']) < 2000) + self.assertTrue(headers['content-type'].startswith('multipart/form-data')) + self.assertEqual(handler.command, 'POST') + self.assertNotIn('\n', headers['authorization']) def test_suite(): - return unittest.makeSuite(uploadTestCase) + return unittest.makeSuite(UploadTestCase) if __name__ == "__main__": unittest.main(defaultTest="test_suite") diff --git a/src/distutils2/tests/test_upload_docs.py b/src/distutils2/tests/test_upload_docs.py new file mode 100644 --- /dev/null +++ b/src/distutils2/tests/test_upload_docs.py @@ -0,0 +1,198 @@ +"""Tests for distutils.command.upload_docs.""" +# -*- encoding: utf8 -*- +import httplib, os, os.path, shutil, sys, tempfile, zipfile +from cStringIO import StringIO + +from distutils2.command import upload_docs as upload_docs_mod +from distutils2.command.upload_docs import (upload_docs, zip_dir, + encode_multipart) +from distutils2.core import Distribution + +from distutils2.errors import DistutilsFileError, DistutilsOptionError + +from distutils2.tests import support +from distutils2.tests.pypi_server import PyPIServer, PyPIServerTestCase +from distutils2.tests.test_config import PyPIRCCommandTestCase +from distutils2.tests.support import unittest + + +EXPECTED_MULTIPART_OUTPUT = "\r\n".join([ +'---x', +'Content-Disposition: form-data; name="a"', +'', +'b', +'---x', +'Content-Disposition: form-data; name="c"', +'', +'d', +'---x', +'Content-Disposition: form-data; name="e"; filename="f"', +'', +'g', +'---x', +'Content-Disposition: form-data; name="h"; filename="i"', +'', +'j', +'---x--', +'', +]) + +PYPIRC = """\ +[distutils] +index-servers = server1 + +[server1] +repository = %s +username = real_slim_shady +password = long_island +""" + +class UploadDocsTestCase(PyPIServerTestCase, PyPIRCCommandTestCase): + + def setUp(self): + super(UploadDocsTestCase, self).setUp() + self.dist = Distribution() + self.dist.metadata['Name'] = "distr-name" + self.cmd = upload_docs(self.dist) + + def test_default_uploaddir(self): + sandbox = tempfile.mkdtemp() + previous = os.getcwd() + os.chdir(sandbox) + try: + os.mkdir("build") + self.prepare_sample_dir("build") + self.cmd.ensure_finalized() + self.assertEqual(self.cmd.upload_dir, os.path.join("build", "docs")) + finally: + os.chdir(previous) + + def prepare_sample_dir(self, sample_dir=None): + if sample_dir is None: + sample_dir = tempfile.mkdtemp() + os.mkdir(os.path.join(sample_dir, "docs")) + self.write_file(os.path.join(sample_dir, "docs", "index.html"), "Ce mortel ennui") + self.write_file(os.path.join(sample_dir, "index.html"), "Oh la la") + return sample_dir + + def test_zip_dir(self): + source_dir = self.prepare_sample_dir() + compressed = zip_dir(source_dir) + + zip_f = zipfile.ZipFile(compressed) + self.assertEqual(zip_f.namelist(), ['index.html', 'docs/index.html']) + + def test_encode_multipart(self): + fields = [("a", "b"), ("c", "d")] + files = [("e", "f", "g"), ("h", "i", "j")] + content_type, body = encode_multipart(fields, files, "-x") + self.assertEqual(content_type, "multipart/form-data; boundary=-x") + self.assertEqual(body, EXPECTED_MULTIPART_OUTPUT) + + def prepare_command(self): + self.cmd.upload_dir = self.prepare_sample_dir() + self.cmd.ensure_finalized() + self.cmd.repository = self.pypi.full_address + self.cmd.username = "username" + self.cmd.password = "password" + + def test_upload(self): + self.prepare_command() + self.cmd.run() + + self.assertEqual(len(self.pypi.requests), 1) + handler, request_data = self.pypi.requests[-1] + self.assertIn("content", request_data) + self.assertIn("Basic", handler.headers.dict['authorization']) + self.assertTrue(handler.headers.dict['content-type'] + .startswith('multipart/form-data;')) + + action, name, content =\ + request_data.split("----------------GHSKFJDLGDS7543FJKLFHRE75642756743254")[1:4] + + # check that we picked the right chunks + self.assertIn('name=":action"', action) + self.assertIn('name="name"', name) + self.assertIn('name="content"', content) + + # check their contents + self.assertIn("doc_upload", action) + self.assertIn("distr-name", name) + self.assertIn("docs/index.html", content) + self.assertIn("Ce mortel ennui", content) + + def test_https_connection(self): + https_called = False + orig_https = upload_docs_mod.httplib.HTTPSConnection + def https_conn_wrapper(*args): + https_called = True + return upload_docs_mod.httplib.HTTPConnection(*args) # the testing server is http + upload_docs_mod.httplib.HTTPSConnection = https_conn_wrapper + try: + self.prepare_command() + self.cmd.run() + self.assertFalse(https_called) + + self.cmd.repository = self.cmd.repository.replace("http", "https") + self.cmd.run() + self.assertFalse(https_called) + finally: + upload_docs_mod.httplib.HTTPSConnection = orig_https + + def test_handling_response(self): + calls = [] + def aggr(*args): + calls.append(args) + self.pypi.default_response_status = '403 Forbidden' + self.prepare_command() + self.cmd.announce = aggr + self.cmd.run() + message, _ = calls[-1] + self.assertIn('Upload failed (403): Forbidden', message) + + calls = [] + self.pypi.default_response_status = '301 Moved Permanently' + self.pypi.default_response_headers.append(("Location", "brand_new_location")) + self.cmd.run() + message, _ = calls[-1] + self.assertIn('brand_new_location', message) + + def test_reads_pypirc_data(self): + self.write_file(self.rc, PYPIRC % self.pypi.full_address) + self.cmd.repository = self.pypi.full_address + self.cmd.upload_dir = self.prepare_sample_dir() + self.cmd.ensure_finalized() + self.assertEqual(self.cmd.username, "real_slim_shady") + self.assertEqual(self.cmd.password, "long_island") + + def test_checks_index_html_presence(self): + self.cmd.upload_dir = self.prepare_sample_dir() + os.remove(os.path.join(self.cmd.upload_dir, "index.html")) + self.assertRaises(DistutilsFileError, self.cmd.ensure_finalized) + + def test_checks_upload_dir(self): + self.cmd.upload_dir = self.prepare_sample_dir() + shutil.rmtree(os.path.join(self.cmd.upload_dir)) + self.assertRaises(DistutilsOptionError, self.cmd.ensure_finalized) + + def test_show_response(self): + orig_stdout = sys.stdout + write_args = [] + class MockStdIn(object): + def write(self, arg): + write_args.append(arg) + sys.stdout = MockStdIn() + try: + self.prepare_command() + self.cmd.show_response = True + self.cmd.run() + finally: + sys.stdout = orig_stdout + self.assertTrue(write_args[0], "should report the response") + self.assertIn(self.pypi.default_response_data + "\n", write_args[0]) + +def test_suite(): + return unittest.makeSuite(UploadDocsTestCase) + +if __name__ == "__main__": + unittest.main(defaultTest="test_suite") diff --git a/src/distutils2/tests/test_util.py b/src/distutils2/tests/test_util.py --- a/src/distutils2/tests/test_util.py +++ b/src/distutils2/tests/test_util.py @@ -4,6 +4,8 @@ from copy import copy from StringIO import StringIO import subprocess +import tempfile +import time from distutils2.errors import (DistutilsPlatformError, DistutilsByteCompileError, @@ -100,7 +102,7 @@ return '/'.join(path) os.path.join = _join - self.assertEquals(convert_path('/home/to/my/stuff'), + self.assertEqual(convert_path('/home/to/my/stuff'), '/home/to/my/stuff') # win @@ -112,9 +114,9 @@ self.assertRaises(ValueError, convert_path, '/home/to/my/stuff') self.assertRaises(ValueError, convert_path, 'home/to/my/stuff/') - self.assertEquals(convert_path('home/to/my/stuff'), + self.assertEqual(convert_path('home/to/my/stuff'), 'home\\to\\my\\stuff') - self.assertEquals(convert_path('.'), + self.assertEqual(convert_path('.'), os.curdir) def test_change_root(self): @@ -127,9 +129,9 @@ return '/'.join(path) os.path.join = _join - self.assertEquals(change_root('/root', '/old/its/here'), + self.assertEqual(change_root('/root', '/old/its/here'), '/root/old/its/here') - self.assertEquals(change_root('/root', 'its/here'), + self.assertEqual(change_root('/root', 'its/here'), '/root/its/here') # windows @@ -146,9 +148,9 @@ return '\\'.join(path) os.path.join = _join - self.assertEquals(change_root('c:\\root', 'c:\\old\\its\\here'), + self.assertEqual(change_root('c:\\root', 'c:\\old\\its\\here'), 'c:\\root\\old\\its\\here') - self.assertEquals(change_root('c:\\root', 'its\\here'), + self.assertEqual(change_root('c:\\root', 'its\\here'), 'c:\\root\\its\\here') # BugsBunny os (it's a great os) @@ -159,7 +161,7 @@ # XXX platforms to be covered: os2, mac def test_split_quoted(self): - self.assertEquals(split_quoted('""one"" "two" \'three\' \\four'), + self.assertEqual(split_quoted('""one"" "two" \'three\' \\four'), ['one', 'two', 'three', 'four']) def test_strtobool(self): @@ -177,7 +179,7 @@ res = rfc822_escape(header) wanted = ('I am a%(8s)spoor%(8s)slonesome%(8s)s' 'header%(8s)s') % {'8s': '\n'+8*' '} - self.assertEquals(res, wanted) + self.assertEqual(res, wanted) def test_find_exe_version(self): # the ld version scheme under MAC OS is: @@ -195,7 +197,7 @@ ('@(#)PROGRAM:ld PROJECT:ld64-95.2.12', '95.2.12')): result = _MAC_OS_X_LD_VERSION.search(output) - self.assertEquals(result.group(1), version) + self.assertEqual(result.group(1), version) def _find_executable(self, name): if name in self._exes: @@ -205,43 +207,43 @@ def test_get_compiler_versions(self): # get_versions calls distutils.spawn.find_executable on # 'gcc', 'ld' and 'dllwrap' - self.assertEquals(get_compiler_versions(), (None, None, None)) + self.assertEqual(get_compiler_versions(), (None, None, None)) # Let's fake we have 'gcc' and it returns '3.4.5' self._exes['gcc'] = 'gcc (GCC) 3.4.5 (mingw special)\nFSF' res = get_compiler_versions() - self.assertEquals(str(res[0]), '3.4.5') + self.assertEqual(str(res[0]), '3.4.5') # and let's see what happens when the version # doesn't match the regular expression # (\d+\.\d+(\.\d+)*) self._exes['gcc'] = 'very strange output' res = get_compiler_versions() - self.assertEquals(res[0], None) + self.assertEqual(res[0], None) # same thing for ld if sys.platform != 'darwin': self._exes['ld'] = 'GNU ld version 2.17.50 20060824' res = get_compiler_versions() - self.assertEquals(str(res[1]), '2.17.50') + self.assertEqual(str(res[1]), '2.17.50') self._exes['ld'] = '@(#)PROGRAM:ld PROJECT:ld64-77' res = get_compiler_versions() - self.assertEquals(res[1], None) + self.assertEqual(res[1], None) else: self._exes['ld'] = 'GNU ld version 2.17.50 20060824' res = get_compiler_versions() - self.assertEquals(res[1], None) + self.assertEqual(res[1], None) self._exes['ld'] = '@(#)PROGRAM:ld PROJECT:ld64-77' res = get_compiler_versions() - self.assertEquals(str(res[1]), '77') + self.assertEqual(str(res[1]), '77') # and dllwrap self._exes['dllwrap'] = 'GNU dllwrap 2.17.50 20060824\nFSF' res = get_compiler_versions() - self.assertEquals(str(res[2]), '2.17.50') + self.assertEqual(str(res[2]), '2.17.50') self._exes['dllwrap'] = 'Cheese Wrap' res = get_compiler_versions() - self.assertEquals(res[2], None) + self.assertEqual(res[2], None) @unittest.skipUnless(hasattr(sys, 'dont_write_bytecode'), 'no dont_write_bytecode support') @@ -257,7 +259,10 @@ def test_newer(self): self.assertRaises(DistutilsFileError, util.newer, 'xxx', 'xxx') - + self.newer_f1 = tempfile.NamedTemporaryFile() + time.sleep(1) + self.newer_f2 = tempfile.NamedTemporaryFile() + self.assertTrue(util.newer(self.newer_f2.name, self.newer_f1.name)) def test_find_packages(self): # let's create a structure we want to scan: @@ -294,7 +299,38 @@ self.write_file(os.path.join(pkg5, '__init__.py')) res = find_packages([root], ['pkg1.pkg2']) - self.assertEquals(set(res), set(['pkg1', 'pkg5', 'pkg1.pkg3', 'pkg1.pkg3.pkg6'])) + self.assertEqual(set(res), set(['pkg1', 'pkg5', 'pkg1.pkg3', 'pkg1.pkg3.pkg6'])) + + @unittest.skipUnless(sys.version > '2.6', 'Need Python 2.6 or more') + def test_run_2to3_on_code(self): + content = "print 'test'" + converted_content = "print('test')" + file_handle = self.mktempfile() + file_name = file_handle.name + file_handle.write(content) + file_handle.flush() + file_handle.seek(0) + from distutils2.util import run_2to3 + run_2to3([file_name]) + new_content = "".join(file_handle.read()) + file_handle.close() + self.assertEquals(new_content, converted_content) + + @unittest.skipUnless(sys.version > '2.6', 'Need Python 2.6 or more') + def test_run_2to3_on_doctests(self): + # to check if text files containing doctests only get converted. + content = ">>> print 'test'\ntest\n" + converted_content = ">>> print('test')\ntest\n\n" + file_handle = self.mktempfile() + file_name = file_handle.name + file_handle.write(content) + file_handle.flush() + file_handle.seek(0) + from distutils2.util import run_2to3 + run_2to3([file_name], doctests_only=True) + new_content = "".join(file_handle.readlines()) + file_handle.close() + self.assertEquals(new_content, converted_content) def test_suite(): diff --git a/src/distutils2/tests/test_version.py b/src/distutils2/tests/test_version.py --- a/src/distutils2/tests/test_version.py +++ b/src/distutils2/tests/test_version.py @@ -3,7 +3,7 @@ import os from distutils2.version import NormalizedVersion as V -from distutils2.version import IrrationalVersionError +from distutils2.version import HugeMajorVersionNumError, IrrationalVersionError from distutils2.version import suggest_normalized_version as suggest from distutils2.version import VersionPredicate from distutils2.tests.support import unittest @@ -22,18 +22,22 @@ (V('1.0.dev345'), '1.0.dev345'), (V('1.0.post456.dev623'), '1.0.post456.dev623')) + def test_repr(self): + + self.assertEqual(repr(V('1.0')), "NormalizedVersion('1.0')") + def test_basic_versions(self): for v, s in self.versions: - self.assertEquals(str(v), s) + self.assertEqual(str(v), s) def test_from_parts(self): for v, s in self.versions: parts = v.parts v2 = V.from_parts(*v.parts) - self.assertEquals(v, v2) - self.assertEquals(str(v), str(v2)) + self.assertEqual(v, v2) + self.assertEqual(str(v), str(v2)) def test_irrational_versions(self): @@ -44,6 +48,12 @@ for s in irrational: self.assertRaises(IrrationalVersionError, V, s) + def test_huge_version(self): + + self.assertEquals(str(V('1980.0')), '1980.0') + self.assertRaises(HugeMajorVersionNumError, V, '1981.0') + self.assertEquals(str(V('1981.0', error_on_huge_major_num=False)), '1981.0') + def test_comparison(self): r""" >>> V('1.2.0') == '1.2' @@ -51,12 +61,33 @@ ... TypeError: cannot compare NormalizedVersion and str + >>> V('1.2') < '1.3' + Traceback (most recent call last): + ... + TypeError: cannot compare NormalizedVersion and str + >>> V('1.2.0') == V('1.2') True >>> V('1.2.0') == V('1.2.3') False + >>> V('1.2.0') != V('1.2.3') + True >>> V('1.2.0') < V('1.2.3') True + >>> V('1.2.0') < V('1.2.0') + False + >>> V('1.2.0') <= V('1.2.0') + True + >>> V('1.2.0') <= V('1.2.3') + True + >>> V('1.2.3') <= V('1.2.0') + False + >>> V('1.2.0') >= V('1.2.0') + True + >>> V('1.2.3') >= V('1.2.0') + True + >>> V('1.2.0') >= V('1.2.3') + False >>> (V('1.0') > V('1.0b2')) True >>> (V('1.0') > V('1.0c2') > V('1.0c1') > V('1.0b2') > V('1.0b1') @@ -96,34 +127,35 @@ def test_suggest_normalized_version(self): - self.assertEquals(suggest('1.0'), '1.0') - self.assertEquals(suggest('1.0-alpha1'), '1.0a1') - self.assertEquals(suggest('1.0c2'), '1.0c2') - self.assertEquals(suggest('walla walla washington'), None) - self.assertEquals(suggest('2.4c1'), '2.4c1') + self.assertEqual(suggest('1.0'), '1.0') + self.assertEqual(suggest('1.0-alpha1'), '1.0a1') + self.assertEqual(suggest('1.0c2'), '1.0c2') + self.assertEqual(suggest('walla walla washington'), None) + self.assertEqual(suggest('2.4c1'), '2.4c1') + self.assertEqual(suggest('v1.0'), '1.0') # from setuptools - self.assertEquals(suggest('0.4a1.r10'), '0.4a1.post10') - self.assertEquals(suggest('0.7a1dev-r66608'), '0.7a1.dev66608') - self.assertEquals(suggest('0.6a9.dev-r41475'), '0.6a9.dev41475') - self.assertEquals(suggest('2.4preview1'), '2.4c1') - self.assertEquals(suggest('2.4pre1') , '2.4c1') - self.assertEquals(suggest('2.1-rc2'), '2.1c2') + self.assertEqual(suggest('0.4a1.r10'), '0.4a1.post10') + self.assertEqual(suggest('0.7a1dev-r66608'), '0.7a1.dev66608') + self.assertEqual(suggest('0.6a9.dev-r41475'), '0.6a9.dev41475') + self.assertEqual(suggest('2.4preview1'), '2.4c1') + self.assertEqual(suggest('2.4pre1') , '2.4c1') + self.assertEqual(suggest('2.1-rc2'), '2.1c2') # from pypi - self.assertEquals(suggest('0.1dev'), '0.1.dev0') - self.assertEquals(suggest('0.1.dev'), '0.1.dev0') + self.assertEqual(suggest('0.1dev'), '0.1.dev0') + self.assertEqual(suggest('0.1.dev'), '0.1.dev0') # we want to be able to parse Twisted # development versions are like post releases in Twisted - self.assertEquals(suggest('9.0.0+r2363'), '9.0.0.post2363') + self.assertEqual(suggest('9.0.0+r2363'), '9.0.0.post2363') # pre-releases are using markers like "pre1" - self.assertEquals(suggest('9.0.0pre1'), '9.0.0c1') + self.assertEqual(suggest('9.0.0pre1'), '9.0.0c1') # we want to be able to parse Tcl-TK # they us "p1" "p2" for post releases - self.assertEquals(suggest('1.4p1'), '1.4.post1') + self.assertEqual(suggest('1.4p1'), '1.4.post1') def test_predicate(self): # VersionPredicate knows how to parse stuff like: @@ -151,15 +183,37 @@ self.assertFalse(VersionPredicate('Hey (<=2.5)').match('2.6.0')) self.assertTrue(VersionPredicate('Hey (>=2.5)').match('2.5.1')) + self.assertRaises(ValueError, VersionPredicate, '') + # XXX need to silent the micro version in this case #assert not VersionPredicate('Ho (<3.0,!=2.6)').match('2.6.3') + def test_is_final(self): + # VersionPredicate knows is a distribution is a final one or not. + final_versions = ('1.0', '1.0.post456') + other_versions = ('1.0.dev1', '1.0a2', '1.0c3') + + for version in final_versions: + self.assertTrue(V(version).is_final) + for version in other_versions: + self.assertFalse(V(version).is_final) + +class VersionWhiteBoxTestCase(unittest.TestCase): + + def test_parse_numdots(self): + # For code coverage completeness, as pad_zeros_length can't be set or + # influenced from the public interface + self.assertEquals(V('1.0')._parse_numdots('1.0', '1.0', + pad_zeros_length=3), + [1, 0, 0]) + + def test_suite(): #README = os.path.join(os.path.dirname(__file__), 'README.txt') #suite = [doctest.DocFileSuite(README), unittest.makeSuite(VersionTestCase)] - suite = [unittest.makeSuite(VersionTestCase)] + suite = [unittest.makeSuite(VersionTestCase), + unittest.makeSuite(VersionWhiteBoxTestCase)] return unittest.TestSuite(suite) if __name__ == "__main__": unittest.main(defaultTest="test_suite") - diff --git a/src/distutils2/util.py b/src/distutils2/util.py --- a/src/distutils2/util.py +++ b/src/distutils2/util.py @@ -1,12 +1,15 @@ """distutils.util -Miscellaneous utility functions -- anything that doesn't fit into -one of the other *util.py modules. +Miscellaneous utility functions. """ __revision__ = "$Id: util.py 77761 2010-01-26 22:46:15Z tarek.ziade $" -import sys, os, string, re +import sys +import os +import string +import re +from copy import copy from fnmatch import fnmatchcase from distutils2.errors import (DistutilsPlatformError, DistutilsFileError, @@ -17,6 +20,7 @@ _PLATFORM = None + def newer(source, target): """Tells if the target is newer than the source. @@ -37,6 +41,7 @@ return os.stat(source).st_mtime > os.stat(target).st_mtime + def get_platform(): """Return a string that identifies the current platform. @@ -48,6 +53,7 @@ _PLATFORM = _sysconfig.get_platform() return _PLATFORM + def set_platform(identifier): """Sets the platform string identifier returned by get_platform(). @@ -57,6 +63,7 @@ global _PLATFORM _PLATFORM = identifier + def convert_path(pathname): """Return 'pathname' as a name that will work on the native filesystem. @@ -125,6 +132,7 @@ _environ_checked = 0 + def check_environ(): """Ensure that 'os.environ' has all the environment variables needed. @@ -147,6 +155,7 @@ _environ_checked = 1 + def subst_vars(s, local_vars): """Perform shell/Perl-style variable substitution on 'string'. @@ -158,7 +167,8 @@ variables not found in either 'local_vars' or 'os.environ'. """ check_environ() - def _subst (match, local_vars=local_vars): + + def _subst(match, local_vars=local_vars): var_name = match.group(1) if var_name in local_vars: return str(local_vars[var_name]) @@ -170,6 +180,7 @@ except KeyError, var: raise ValueError("invalid variable '$%s'" % var) + def grok_environment_error(exc, prefix="error: "): """Generate a useful error message from an EnvironmentError. @@ -196,12 +207,14 @@ # Needed by 'split_quoted()' _wordchars_re = _squote_re = _dquote_re = None + def _init_regex(): global _wordchars_re, _squote_re, _dquote_re _wordchars_re = re.compile(r'[^\\\'\"%s ]*' % string.whitespace) _squote_re = re.compile(r"'(?:[^'\\]|\\.)*'") _dquote_re = re.compile(r'"(?:[^"\\]|\\.)*"') + def split_quoted(s): """Split a string up according to Unix shell-like rules for quotes and backslashes. @@ -217,7 +230,8 @@ # This is a nice algorithm for splitting up a single string, since it # doesn't require character-by-character examination. It was a little # bit of a brain-bender to get it working right, though... - if _wordchars_re is None: _init_regex() + if _wordchars_re is None: + _init_regex() s = s.strip() words = [] @@ -237,8 +251,8 @@ elif s[end] == '\\': # preserve whatever is being escaped; # will become part of the current word - s = s[:end] + s[end+1:] - pos = end+1 + s = s[:end] + s[end + 1:] + pos = end + 1 else: if s[end] == "'": # slurp singly-quoted string @@ -253,7 +267,7 @@ raise ValueError("bad string (mismatched %s quotes?)" % s[end]) (beg, end) = m.span() - s = s[:beg] + s[beg+1:end-1] + s[end:] + s = s[:beg] + s[beg + 1:end - 1] + s[end:] pos = m.end() - 2 if pos >= len(s): @@ -296,7 +310,7 @@ elif val in ('n', 'no', 'f', 'false', 'off', '0'): return 0 else: - raise ValueError, "invalid truth value %r" % (val,) + raise ValueError("invalid truth value %r" % (val,)) def byte_compile(py_files, optimize=0, force=0, prefix=None, base_dir=None, @@ -350,12 +364,8 @@ # "Indirect" byte-compilation: write a temporary script and then # run it with the appropriate flags. if not direct: - try: - from tempfile import mkstemp - (script_fd, script_name) = mkstemp(".py") - except ImportError: - from tempfile import mktemp - (script_fd, script_name) = None, mktemp(".py") + from tempfile import mkstemp + script_fd, script_name = mkstemp(".py") log.info("writing byte-compilation script '%s'", script_name) if not dry_run: if script_fd is not None: @@ -363,43 +373,50 @@ else: script = open(script_name, "w") - script.write("""\ -from distutils.util import byte_compile + try: + script.write("""\ +from distutils2.util import byte_compile files = [ """) - # XXX would be nice to write absolute filenames, just for - # safety's sake (script should be more robust in the face of - # chdir'ing before running it). But this requires abspath'ing - # 'prefix' as well, and that breaks the hack in build_lib's - # 'byte_compile()' method that carefully tacks on a trailing - # slash (os.sep really) to make sure the prefix here is "just - # right". This whole prefix business is rather delicate -- the - # problem is that it's really a directory, but I'm treating it - # as a dumb string, so trailing slashes and so forth matter. + # XXX would be nice to write absolute filenames, just for + # safety's sake (script should be more robust in the face of + # chdir'ing before running it). But this requires abspath'ing + # 'prefix' as well, and that breaks the hack in build_lib's + # 'byte_compile()' method that carefully tacks on a trailing + # slash (os.sep really) to make sure the prefix here is "just + # right". This whole prefix business is rather delicate -- the + # problem is that it's really a directory, but I'm treating it + # as a dumb string, so trailing slashes and so forth matter. - #py_files = map(os.path.abspath, py_files) - #if prefix: - # prefix = os.path.abspath(prefix) + #py_files = map(os.path.abspath, py_files) + #if prefix: + # prefix = os.path.abspath(prefix) - script.write(",\n".join(map(repr, py_files)) + "]\n") - script.write(""" + script.write(",\n".join(map(repr, py_files)) + "]\n") + script.write(""" byte_compile(files, optimize=%r, force=%r, prefix=%r, base_dir=%r, verbose=%r, dry_run=0, direct=1) """ % (optimize, force, prefix, base_dir, verbose)) - script.close() + finally: + script.close() cmd = [sys.executable, script_name] if optimize == 1: cmd.insert(1, "-O") elif optimize == 2: cmd.insert(1, "-OO") - spawn(cmd, dry_run=dry_run) - execute(os.remove, (script_name,), "removing %s" % script_name, - dry_run=dry_run) + + env = copy(os.environ) + env['PYTHONPATH'] = ':'.join(sys.path) + try: + spawn(cmd, dry_run=dry_run, env=env) + finally: + execute(os.remove, (script_name,), "removing %s" % script_name, + dry_run=dry_run) # "Direct" byte-compilation: use the py_compile module to compile # right here, right now. Note that the script generated in indirect @@ -447,7 +464,9 @@ return sep.join(lines) _RE_VERSION = re.compile('(\d+\.\d+(\.\d+)*)') -_MAC_OS_X_LD_VERSION = re.compile('^@\(#\)PROGRAM:ld PROJECT:ld64-((\d+)(\.\d+)*)') +_MAC_OS_X_LD_VERSION = re.compile('^@\(#\)PROGRAM:ld ' + 'PROJECT:ld64-((\d+)(\.\d+)*)') + def _find_ld_version(): """Finds the ld version. The version scheme differs under Mac OSX.""" @@ -456,6 +475,7 @@ else: return _find_exe_version('ld -v') + def _find_exe_version(cmd, pattern=_RE_VERSION): """Find the version of an executable by running `cmd` in the shell. @@ -485,6 +505,7 @@ return None return result.group(1) + def get_compiler_versions(): """Returns a tuple providing the versions of gcc, ld and dllwrap @@ -496,6 +517,7 @@ dllwrap = _find_exe_version('dllwrap --version') return gcc, ld, dllwrap + def newer_group(sources, target, missing='error'): """Return true if 'target' is out-of-date with respect to any file listed in 'sources'. @@ -535,14 +557,18 @@ return False + def write_file(filename, contents): """Create a file with the specified name and write 'contents' (a sequence of strings without line terminators) to it. """ - f = open(filename, "w") - for line in contents: - f.write(line + "\n") - f.close() + try: + f = open(filename, "w") + for line in contents: + f.write(line + "\n") + finally: + f.close() + def _is_package(path): """Returns True if path is a package (a dir with an __init__ file.""" @@ -550,6 +576,7 @@ return False return os.path.isfile(os.path.join(path, '__init__.py')) + def _under(path, root): path = path.split(os.sep) root = root.split(os.sep) @@ -560,12 +587,14 @@ return False return True + def _package_name(root_path, path): """Returns a dotted package name, given a subpath.""" if not _under(path, root_path): raise ValueError('"%s" is not a subpath of "%s"' % (path, root_path)) return path[len(root_path) + 1:].replace(os.sep, '.') + def find_packages(paths=('.',), exclude=()): """Return a list all Python packages found recursively within directories 'paths' @@ -580,6 +609,7 @@ """ packages = [] discarded = [] + def _discarded(path): for discard in discarded: if _under(path, discard): @@ -611,3 +641,50 @@ packages.append(package_name) return packages + +# utility functions for 2to3 support + +def run_2to3(files, doctests_only=False, fixer_names=None, options=None, + explicit=None): + """ Wrapper function around the refactor() class which + performs the conversions on a list of python files. + Invoke 2to3 on a list of Python files. The files should all come + from the build area, as the modification is done in-place.""" + + if not files: + return + + # Make this class local, to delay import of 2to3 + from lib2to3.refactor import get_fixers_from_package + from distutils2.converter.refactor import DistutilsRefactoringTool + + if fixer_names is None: + fixer_names = get_fixers_from_package('lib2to3.fixes') + + r = DistutilsRefactoringTool(fixer_names, options=options) + if doctests_only: + r.refactor(files, doctests_only=True, write=True) + else: + r.refactor(files, write=True) + + +class Mixin2to3: + """ Wrapper class for commands that run 2to3. + To configure 2to3, setup scripts may either change + the class variables, or inherit from this class + to override how 2to3 is invoked. + """ + # provide list of fixers to run. + # defaults to all from lib2to3.fixers + fixer_names = None + + # options dictionary + options = None + + # list of fixers to invoke even though they are marked as explicit + explicit = None + + def run_2to3(self, files, doctests_only=False): + """ Issues a call to util.run_2to3. """ + return run_2to3(files, doctests_only, self.fixer_names, + self.options, self.explicit) diff --git a/src/distutils2/version.py b/src/distutils2/version.py --- a/src/distutils2/version.py +++ b/src/distutils2/version.py @@ -36,6 +36,7 @@ (?P(\.post(?P\d+))?(\.dev(?P\d+))?)? $''', re.VERBOSE) + class NormalizedVersion(object): """A rational version. @@ -61,7 +62,8 @@ @param error_on_huge_major_num {bool} Whether to consider an apparent use of a year or full date as the major version number an error. Default True. One of the observed patterns on PyPI before - the introduction of `NormalizedVersion` was version numbers like this: + the introduction of `NormalizedVersion` was version numbers like + this: 2009.01.03 20040603 2005.01 @@ -71,6 +73,7 @@ the possibility of using a version number like "1.0" (i.e. where the major number is less than that huge major number). """ + self.is_final = True # by default, consider a version as final. self._parse(s, error_on_huge_major_num) @classmethod @@ -101,6 +104,7 @@ block += self._parse_numdots(groups.get('prerelversion'), s, pad_zeros_length=1) parts.append(tuple(block)) + self.is_final = False else: parts.append(_FINAL_MARKER) @@ -115,6 +119,7 @@ postdev.append(_FINAL_MARKER[0]) if dev is not None: postdev.extend(['dev', int(dev)]) + self.is_final = False parts.append(tuple(postdev)) else: parts.append(_FINAL_MARKER) @@ -204,6 +209,7 @@ # See http://docs.python.org/reference/datamodel#object.__hash__ __hash__ = object.__hash__ + def suggest_normalized_version(s): """Suggest a normalized version close to the given version string. @@ -215,7 +221,7 @@ on observation of versions currently in use on PyPI. Given a dump of those version during PyCon 2009, 4287 of them: - 2312 (53.93%) match NormalizedVersion without change - - with the automatic suggestion + with the automatic suggestion - 3474 (81.04%) match when using this suggestion method @param s {str} An irrational version string. @@ -305,7 +311,6 @@ # PyPI stats: ~21 (0.62%) better rs = re.sub(r"\.?(pre|preview|-c)(\d+)$", r"c\g<2>", rs) - # Tcl/Tk uses "px" for their post release markers rs = re.sub(r"p(\d+)$", r".post\1", rs) @@ -322,6 +327,7 @@ _PLAIN_VERSIONS = re.compile(r"^\s*(.*)\s*$") _SPLIT_CMP = re.compile(r"^\s*(<=|>=|<|>|!=|==)\s*([^\s,]+)\s*$") + def _split_predicate(predicate): match = _SPLIT_CMP.match(predicate) if match is None: @@ -368,26 +374,25 @@ return False return True + class _Versions(VersionPredicate): def __init__(self, predicate): predicate = predicate.strip() match = _PLAIN_VERSIONS.match(predicate) - if match is None: - raise ValueError('Bad predicate "%s"' % predicate) self.name = None predicates = match.groups()[0] self.predicates = [_split_predicate(pred.strip()) for pred in predicates.split(',')] + class _Version(VersionPredicate): def __init__(self, predicate): predicate = predicate.strip() match = _PLAIN_VERSIONS.match(predicate) - if match is None: - raise ValueError('Bad predicate "%s"' % predicate) self.name = None self.predicates = _split_predicate(match.groups()[0]) + def is_valid_predicate(predicate): try: VersionPredicate(predicate) @@ -396,6 +401,7 @@ else: return True + def is_valid_versions(predicate): try: _Versions(predicate) @@ -404,6 +410,7 @@ else: return True + def is_valid_version(predicate): try: _Version(predicate) @@ -411,4 +418,3 @@ return False else: return True - diff --git a/src/runtests-cov.py b/src/runtests-cov.py new file mode 100755 --- /dev/null +++ b/src/runtests-cov.py @@ -0,0 +1,119 @@ +#!/usr/bin/env python +"""Tests for distutils2. + +The tests for distutils2 are defined in the distutils2.tests package. +""" + +import sys +from os.path import dirname, islink, realpath +from optparse import OptionParser + +def ignore_prefixes(module): + """ Return a list of prefixes to ignore in the coverage report if + we want to completely skip `module`. + """ + # A function like that is needed because some GNU/Linux + # distributions, such a Ubuntu, really like to build link farm in + # /usr/lib in order to save a few bytes on the disk. + dirnames = [dirname(module.__file__)] + + pymod = module.__file__.rstrip("c") + if islink(pymod): + dirnames.append(dirname(realpath(pymod))) + return dirnames + +def parse_opts(): + parser = OptionParser(usage="%prog [OPTIONS]", + description="run the distutils2 unittests") + + parser.add_option("-q", "--quiet", help="do not print verbose messages", + action="store_true", default=False) + parser.add_option("-c", "--coverage", action="store_true", default=False, + help="produce a coverage report at the end of the run") + parser.add_option("-r", "--report", action="store_true", default=False, + help="produce a coverage report from the last test run") + parser.add_option("-m", "--show-missing", action="store_true", + default=False, + help=("Show line numbers of statements in each module " + "that weren't executed.")) + + opts, args = parser.parse_args() + return opts, args + +def coverage_report(opts): + import coverage + import unittest2 + cov = coverage.coverage() + cov.load() + + prefixes = ["runtests", "distutils2/tests", "distutils2/_backport"] + prefixes += ignore_prefixes(unittest2) + + try: + import docutils + prefixes += ignore_prefixes(docutils) + except ImportError: + # that module is completely optional + pass + + try: + import roman + prefixes += ignore_prefixes(roman) + except ImportError: + # that module is also completely optional + pass + + cov.report(omit_prefixes=prefixes, show_missing=opts.show_missing) + + +def test_main(): + opts, args = parse_opts() + verbose = not opts.quiet + ret = 0 + + if opts.coverage or opts.report: + import coverage + + if opts.coverage: + cov = coverage.coverage() + cov.erase() + cov.start() + if not opts.report: + ret = run_tests(verbose) + if opts.coverage: + cov.stop() + cov.save() + + if opts.report or opts.coverage: + coverage_report(opts) + + return ret + +def run_tests(verbose): + import distutils2.tests + from distutils2.tests import run_unittest, reap_children, TestFailed + from distutils2._backport.tests import test_suite as btest_suite + # XXX just supporting -q right now to enable detailed/quiet output + if len(sys.argv) > 1: + verbose = sys.argv[-1] != '-q' + else: + verbose = 1 + try: + try: + run_unittest([distutils2.tests.test_suite(), btest_suite()], + verbose_=verbose) + return 0 + except TestFailed: + return 1 + finally: + reap_children() + +if __name__ == "__main__": + try: + from distutils2.tests.support import unittest + except ImportError: + sys.stderr.write('Error: You have to install unittest2') + sys.exit(1) + + sys.exit(test_main()) + diff --git a/src/runtests.py b/src/runtests.py --- a/src/runtests.py +++ b/src/runtests.py @@ -1,6 +1,6 @@ """Tests for distutils2. -The tests for distutils2 are defined in the distutils2.tests package; +The tests for distutils2 are defined in the distutils2.tests package. """ import sys @@ -8,8 +8,7 @@ import distutils2.tests from distutils2.tests import run_unittest, reap_children, TestFailed from distutils2._backport.tests import test_suite as btest_suite - # just supporting -q right now - # to enable detailed/quiet output + # XXX just supporting -q right now to enable detailed/quiet output if len(sys.argv) > 1: verbose = sys.argv[-1] != '-q' else: @@ -17,7 +16,7 @@ try: try: run_unittest([distutils2.tests.test_suite(), btest_suite()], - verbose_=verbose) + verbose_=verbose) return 0 except TestFailed: return 1 @@ -28,7 +27,7 @@ try: from distutils2.tests.support import unittest except ImportError: - print('Error: You have to install unittest2') + sys.stderr.write('Error: You have to install unittest2') sys.exit(1) sys.exit(test_main()) diff --git a/src/setup.py b/src/setup.py --- a/src/setup.py +++ b/src/setup.py @@ -3,8 +3,12 @@ __revision__ = "$Id$" import sys import os +import re -from distutils2.core import setup +from distutils2 import __version__ as VERSION +from distutils2 import log +from distutils2.core import setup, Extension +from distutils2.compiler.ccompiler import new_compiler from distutils2.command.sdist import sdist from distutils2.command.install import install from distutils2 import __version__ as VERSION @@ -31,6 +35,7 @@ DEV_SUFFIX = '.dev%d' % get_tip_revision('..') + class install_hg(install): user_options = install.user_options + [ @@ -62,10 +67,141 @@ self.distribution.metadata.version += DEV_SUFFIX sdist.run(self) + +# additional paths to check, set from the command line +SSL_INCDIR = '' # --openssl-incdir= +SSL_LIBDIR = '' # --openssl-libdir= +SSL_DIR = '' # --openssl-prefix= + +def add_dir_to_list(dirlist, dir): + """Add the directory 'dir' to the list 'dirlist' (at the front) if + 'dir' actually exists and is a directory. If 'dir' is already in + 'dirlist' it is moved to the front. + """ + if dir is not None and os.path.isdir(dir) and dir not in dirlist: + if dir in dirlist: + dirlist.remove(dir) + dirlist.insert(0, dir) + + +def prepare_hashlib_extensions(): + """Decide which C extensions to build and create the appropriate + Extension objects to build them. Return a list of Extensions. + """ + # this CCompiler object is only used to locate include files + compiler = new_compiler() + + # Ensure that these paths are always checked + if os.name == 'posix': + add_dir_to_list(compiler.library_dirs, '/usr/local/lib') + add_dir_to_list(compiler.include_dirs, '/usr/local/include') + + add_dir_to_list(compiler.library_dirs, '/usr/local/ssl/lib') + add_dir_to_list(compiler.include_dirs, '/usr/local/ssl/include') + + add_dir_to_list(compiler.library_dirs, '/usr/contrib/ssl/lib') + add_dir_to_list(compiler.include_dirs, '/usr/contrib/ssl/include') + + add_dir_to_list(compiler.library_dirs, '/usr/lib') + add_dir_to_list(compiler.include_dirs, '/usr/include') + + # look in command line supplied paths + if SSL_LIBDIR: + add_dir_to_list(compiler.library_dirs, SSL_LIBDIR) + if SSL_INCDIR: + add_dir_to_list(compiler.include_dirs, SSL_INCDIR) + if SSL_DIR: + if os.name == 'nt': + add_dir_to_list(compiler.library_dirs, os.path.join(SSL_DIR, 'out32dll')) + # prefer the static library + add_dir_to_list(compiler.library_dirs, os.path.join(SSL_DIR, 'out32')) + else: + add_dir_to_list(compiler.library_dirs, os.path.join(SSL_DIR, 'lib')) + add_dir_to_list(compiler.include_dirs, os.path.join(SSL_DIR, 'include')) + + oslibs = {'posix': ['ssl', 'crypto'], + 'nt': ['libeay32', 'gdi32', 'advapi32', 'user32']} + + if os.name not in oslibs: + sys.stderr.write( + 'unknown operating system, impossible to compile _hashlib') + sys.exit(1) + + exts = [] + + ssl_inc_dirs = [] + ssl_incs = [] + for inc_dir in compiler.include_dirs: + f = os.path.join(inc_dir, 'openssl', 'ssl.h') + if os.path.exists(f): + ssl_incs.append(f) + ssl_inc_dirs.append(inc_dir) + + ssl_lib = compiler.find_library_file(compiler.library_dirs, oslibs[os.name][0]) + + # find out which version of OpenSSL we have + openssl_ver = 0 + openssl_ver_re = re.compile( + '^\s*#\s*define\s+OPENSSL_VERSION_NUMBER\s+(0x[0-9a-fA-F]+)' ) + ssl_inc_dir = '' + for ssl_inc_dir in ssl_inc_dirs: + name = os.path.join(ssl_inc_dir, 'openssl', 'opensslv.h') + if os.path.isfile(name): + try: + incfile = open(name, 'r') + for line in incfile: + m = openssl_ver_re.match(line) + if m: + openssl_ver = int(m.group(1), 16) + break + except IOError: + pass + + # first version found is what we'll use + if openssl_ver: + break + + if (ssl_inc_dir and ssl_lib is not None and openssl_ver >= 0x00907000): + + log.info('Using OpenSSL version 0x%08x from', openssl_ver) + log.info(' Headers:\t%s', ssl_inc_dir) + log.info(' Library:\t%s', ssl_lib) + + # The _hashlib module wraps optimized implementations + # of hash functions from the OpenSSL library. + exts.append(Extension('_hashlib', ['_hashopenssl.c'], + include_dirs = [ssl_inc_dir], + library_dirs = [os.path.dirname(ssl_lib)], + libraries = oslibs[os.name])) + else: + exts.append(Extension('_sha', ['shamodule.c']) ) + exts.append(Extension('_md5', + sources=['md5module.c', 'md5.c'], + depends=['md5.h']) ) + + if (not ssl_lib or openssl_ver < 0x00908000): + # OpenSSL doesn't do these until 0.9.8 so we'll bring our own + exts.append(Extension('_sha256', ['sha256module.c'])) + exts.append(Extension('_sha512', ['sha512module.c'])) + + def prepend_modules(filename): + return os.path.join('Modules', filename) + + # all the C code is in the Modules subdirectory, prepend the path + for ext in exts: + ext.sources = [prepend_modules(fn) for fn in ext.sources] + if hasattr(ext, 'depends') and ext.depends is not None: + ext.depends = [prepend_modules(fn) for fn in ext.depends] + + return exts + setup_kwargs = {} if sys.version < '2.6': setup_kwargs['scripts'] = ['distutils2/mkpkg.py'] +if sys.version < '2.5': + setup_kwargs['ext_modules'] = prepare_hashlib_extensions() + _CLASSIFIERS = """\ Development Status :: 3 - Alpha Intended Audience :: Developers @@ -77,26 +213,23 @@ Topic :: System :: Systems Administration Topic :: Utilities""" -setup (name="Distutils2", - version=VERSION, - summary="Python Distribution Utilities", - keywords=['packaging', 'distutils'], - author="Tarek Ziade", - author_email="tarek at ziade.org", - home_page="http://bitbucket.org/tarek/distutils2/wiki/Home", - license="PSF", - description=README, - classifier=_CLASSIFIERS.split('\n'), - packages=find_packages(), - cmdclass={'sdist': sdist_hg, 'install': install_hg}, - package_data={'distutils2._backport': ['sysconfig.cfg']}, - project_url=[('Mailing-list', +setup(name="Distutils2", + version=VERSION, + summary="Python Distribution Utilities", + keywords=['packaging', 'distutils'], + author="Tarek Ziade", + author_email="tarek at ziade.org", + home_page="http://bitbucket.org/tarek/distutils2/wiki/Home", + license="PSF", + description=README, + classifier=_CLASSIFIERS.split('\n'), + packages=find_packages(), + cmdclass={'sdist': sdist_hg, 'install': install_hg}, + package_data={'distutils2._backport': ['sysconfig.cfg']}, + project_url=[('Mailing list', 'http://mail.python.org/mailman/listinfo/distutils-sig/'), - ('Documentation', - 'http://packages.python.org/Distutils2'), - ('Repository', 'http://hg.python.org/distutils2'), - ('Bug tracker', 'http://bugs.python.org')], - **setup_kwargs - ) - - + ('Documentation', + 'http://packages.python.org/Distutils2'), + ('Repository', 'http://hg.python.org/distutils2'), + ('Bug tracker', 'http://bugs.python.org')], + **setup_kwargs) diff --git a/src/tests.sh b/src/tests.sh --- a/src/tests.sh +++ b/src/tests.sh @@ -1,28 +1,40 @@ #!/bin/sh -echo -n "Running tests for Python 2.4..." -python2.4 runtests.py -q > /dev/null 2> /dev/null +echo -n "Running tests for Python 2.4... " +rm -rf _hashlib.so +python2.4 setup.py build_ext -i -q 2> /dev/null > /dev/null +python2.4 -Wd runtests.py -q 2> /dev/null if [ $? -ne 0 ];then echo "Failed" - exit $1 + exit 1 else echo "Success" fi -echo -n "Running tests for Python 2.5..." -python2.5 runtests.py -q > /dev/null 2> /dev/null +echo -n "Running tests for Python 2.5... " +rm -rf _hashlib.so +python2.5 setup.py build_ext -i -q 2> /dev/null > /dev/null +python2.5 -Wd runtests.py -q 2> /dev/null if [ $? -ne 0 ];then echo "Failed" - exit $1 + exit 1 else echo "Success" fi -echo -n "Running tests for Python 2.6..." -python2.6 runtests.py -q > /dev/null 2> /dev/null +echo -n "Running tests for Python 2.6... " +python2.6 -Wd -bb -3 runtests.py -q 2> /dev/null if [ $? -ne 0 ];then echo "Failed" - exit $1 + exit 1 else echo "Success" fi +echo -n "Running tests for Python 2.7... " +python2.7 -Wd -bb -3 runtests.py -q 2> /dev/null +if [ $? -ne 0 ];then + echo "Failed" + exit 1 +else + echo "Success" +fi -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:46 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:46 +0200 Subject: [Python-checkins] distutils2: Add a way to search for some packages by their names, using the simple index. Message-ID: tarek.ziade pushed 93ad7f1cf400 to distutils2: http://hg.python.org/distutils2/rev/93ad7f1cf400 changeset: 456:93ad7f1cf400 user: Alexis Metaireau date: Fri Jul 23 19:35:34 2010 +0200 summary: Add a way to search for some packages by their names, using the simple index. files: src/distutils2/index/simple.py, src/distutils2/tests/pypiserver/project_list/simple/index.html, src/distutils2/tests/test_index_simple.py diff --git a/src/distutils2/index/simple.py b/src/distutils2/index/simple.py --- a/src/distutils2/index/simple.py +++ b/src/distutils2/index/simple.py @@ -55,6 +55,25 @@ return _socket_timeout return _socket_timeout +def with_mirror_support(): + """Decorator that makes the mirroring support easier""" + def wrapper(func): + def wrapped(self, *args, **kwargs): + try: + return func(self, *args, **kwargs) + except DownloadError: + # if an error occurs, try with the next index_url + if self._mirrors_tries >= self._mirrors_max_tries: + try: + self._switch_to_next_mirror() + except KeyError: + raise UnableToDownload("Tried all mirrors") + else: + self._mirrors_tries += 1 + self._releases.clear() + return wrapped(self, *args, **kwargs) + return wrapped + return wrapper class Crawler(IndexClient): """Provides useful tools to request the Python Package Index simple API. @@ -110,7 +129,20 @@ # on one) self._processed_urls = [] self._releases = {} - + + @with_mirror_support() + def search(self, name=None, **kwargs): + """Search the index for projects containing the given name""" + index = self._open_url(self.index_url) + + projectname = re.compile("""]*>(.?[^<]*%s.?[^<]*)""" % name, + flags=re.I) + matching_projects = [] + for match in projectname.finditer(index.read()): + matching_projects.append(match.group(1)) + + return matching_projects + def _search_for_releases(self, requirements): """Search for distributions and return a ReleaseList object containing the results @@ -275,26 +307,15 @@ if self._is_browsable(url): yield (url, False) + @with_mirror_support() def _process_index_page(self, name): """Find and process a PyPI page for the given project name. :param name: the name of the project to find the page """ - try: - # Browse and index the content of the given PyPI page. - url = self.index_url + name + "/" - self._process_url(url, name) - except DownloadError: - # if an error occurs, try with the next index_url - if self._mirrors_tries >= self._mirrors_max_tries: - try: - self._switch_to_next_mirror() - except KeyError: - raise UnableToDownload("Tried all mirrors") - else: - self._mirrors_tries += 1 - self._releases.clear() - self._process_index_page(name) + # Browse and index the content of the given PyPI page. + url = self.index_url + name + "/" + self._process_url(url, name) @socket_timeout() def _open_url(self, url): diff --git a/src/distutils2/tests/pypiserver/project_list/simple/index.html b/src/distutils2/tests/pypiserver/project_list/simple/index.html new file mode 100644 --- /dev/null +++ b/src/distutils2/tests/pypiserver/project_list/simple/index.html @@ -0,0 +1,5 @@ +FooBar-bar +Foobar-baz +Baz-FooBar +Baz +Foo diff --git a/src/distutils2/tests/test_index_simple.py b/src/distutils2/tests/test_index_simple.py --- a/src/distutils2/tests/test_index_simple.py +++ b/src/distutils2/tests/test_index_simple.py @@ -300,6 +300,15 @@ self.assertIn('http://example.org/some/simpleurl', found_links) self.assertIn('http://example.org/some/download', found_links) + @use_pypi_server("project_list") + def test_search(self, server): + # we can search the index for some projects, on their names + # the case used no matters here + crawler = self._get_simple_crawler(server) + projects = crawler.search("Foobar") + self.assertListEqual(['FooBar-bar', 'Foobar-baz', 'Baz-FooBar'], + projects) + def test_suite(): return unittest.makeSuite(SimpleCrawlerTestCase) -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:46 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:46 +0200 Subject: [Python-checkins] distutils2: Add a wrapper around the two client implementations. Message-ID: tarek.ziade pushed d92211963592 to distutils2: http://hg.python.org/distutils2/rev/d92211963592 changeset: 460:d92211963592 user: Alexis Metaireau date: Tue Jul 27 15:22:15 2010 +0200 summary: Add a wrapper around the two client implementations. files: src/distutils2/index/wrapper.py diff --git a/src/distutils2/index/wrapper.py b/src/distutils2/index/wrapper.py new file mode 100644 --- /dev/null +++ b/src/distutils2/index/wrapper.py @@ -0,0 +1,89 @@ +import xmlrpc +import simple + +_WRAPPER_MAPPINGS = {'get_release': 'simple', + 'get_releases': 'simple', + 'search_projects': 'simple', } + +_WRAPPER_INDEXES = {'xmlrpc': xmlrpc.Client, + 'simple': simple.Crawler} + +def switch_index_if_fails(func, wrapper): + """Decorator that switch of index (for instance from xmlrpc to simple) + if the first mirror return an empty list or raises an exception. + """ + def decorator(*args, **kwargs): + retry = True + exception = None + methods = [func] + for f in wrapper._indexes.values(): + if f != func.im_self and hasattr(f, func.__name__): + methods.append(getattr(f, func.__name__)) + for method in methods: + try: + response = method(*args, **kwargs) + retry = False + except Exception, e: + exception = e + if not retry: + break + if retry and exception: + raise exception + else: + return response + return decorator + + +class ClientWrapper(object): + """Wrapper around simple and xmlrpc clients, + + Choose the best implementation to use depending the needs. + If one of the indexes returns an error, try to use others indexes. + + :param index: tell wich index to rely on by default. + :param index_classes: a dict of name:class to use as indexes. + :param indexes: a dict of name:index already instanciated + :param mappings: the mappings to use for this wrapper + """ + + def __init__(self, default_index='simple', index_classes=_WRAPPER_INDEXES, + indexes={}, mappings=_WRAPPER_MAPPINGS): + self._projects = {} + self._mappings = mappings + self._indexes = indexes + self._default_index = default_index + + # instanciate the classes and set their _project attribute to the one + # of the wrapper. + for name, cls in index_classes.items(): + obj = self._indexes.setdefault(name, cls()) + obj._projects = self._projects + + def __getattr__(self, method_name): + """When asking for methods of the wrapper, return the implementation of + the wrapped classes, depending the mapping. + + Decorate the methods to switch of implementation if an error occurs + """ + real_method = None + if method_name in _WRAPPER_MAPPINGS: + obj = self._indexes[_WRAPPER_MAPPINGS[method_name]] + real_method = getattr(obj, method_name) + else: + # the method is not defined in the mappings, so we try first to get + # it via the default index, and rely on others if needed. + try: + real_method = getattr(self._indexes[self._default_index], + method_name) + except AttributeError: + other_indexes = [i for i in self._indexes + if i != self._default_index] + for index in other_indexes: + real_method = getattr(self._indexes[index], method_name, None) + if real_method: + break + if real_method: + return switch_index_if_fails(real_method, self) + else: + raise AttributeError("No index have attribute '%s'" % method_name) + -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:46 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:46 +0200 Subject: [Python-checkins] distutils2: Add an unpack method to distributions. Message-ID: tarek.ziade pushed ec647abcb17d to distutils2: http://hg.python.org/distutils2/rev/ec647abcb17d changeset: 459:ec647abcb17d user: Alexis Metaireau date: Mon Jul 26 17:58:36 2010 +0200 summary: Add an unpack method to distributions. files: src/distutils2/index/dist.py, src/distutils2/util.py diff --git a/src/distutils2/index/dist.py b/src/distutils2/index/dist.py --- a/src/distutils2/index/dist.py +++ b/src/distutils2/index/dist.py @@ -10,10 +10,13 @@ distributions contains download related informations. """ +import mimetypes import re +import tarfile import tempfile import urllib import urlparse +import zipfile try: import hashlib @@ -25,6 +28,7 @@ CantParseArchiveName) from distutils2.version import suggest_normalized_version, NormalizedVersion from distutils2.metadata import DistributionMetadata +from distutils2.util import untar_file, unzip_file, splitext __all__ = ['ReleaseInfo', 'DistInfo', 'ReleasesList', 'get_infos_from_url'] @@ -247,6 +251,7 @@ """Download the distribution to a path, and return it. If the path is given in path, use this, otherwise, generates a new one + Return the download location. """ if path is None: path = tempfile.mkdtemp() @@ -261,6 +266,30 @@ self._check_md5(filename) return self.downloaded_location + def unpack(self, path=None): + """Unpack the distribution to the given path. + + If not destination is given, creates a temporary location. + + Returns the location of the extracted files (root). + """ + if path is None: + path = tempfile.mkdtemp() + + filename = self.download() + content_type = mimetypes.guess_type(filename)[0] + + if (content_type == 'application/zip' + or filename.endswith('.zip') + or filename.endswith('.pybundle') + or zipfile.is_zipfile(filename)): + unzip_file(filename, path, flatten=not filename.endswith('.pybundle')) + elif (content_type == 'application/x-gzip' + or tarfile.is_tarfile(filename) + or splitext(filename)[1].lower() in ('.tar', '.tar.gz', '.tar.bz2', '.tgz', '.tbz')): + untar_file(filename, path) + return path + def _check_md5(self, filename): """Check that the md5 checksum of the given file matches the one in url param""" @@ -275,7 +304,7 @@ % (hashval.hexdigest(), expected_hashval)) def __repr__(self): - return "%s %s %s" % ( + return "<%s %s %s>" % ( self.release.name, self.release.version, self.dist_type or "") diff --git a/src/distutils2/util.py b/src/distutils2/util.py --- a/src/distutils2/util.py +++ b/src/distutils2/util.py @@ -5,10 +5,14 @@ __revision__ = "$Id: util.py 77761 2010-01-26 22:46:15Z tarek.ziade $" +import os +import posixpath +import re +import string import sys -import os -import string -import re +import shutil +import tarfile +import zipfile from copy import copy from fnmatch import fnmatchcase @@ -688,3 +692,116 @@ """ Issues a call to util.run_2to3. """ return run_2to3(files, doctests_only, self.fixer_names, self.options, self.explicit) + + +def splitext(path): + """Like os.path.splitext, but take off .tar too""" + base, ext = posixpath.splitext(path) + if base.lower().endswith('.tar'): + ext = base[-4:] + ext + base = base[:-4] + return base, ext + + +def unzip_file(filename, location, flatten=True): + """Unzip the file (zip file located at filename) to the destination + location""" + if not os.path.exists(location): + os.makedirs(location) + zipfp = open(filename, 'rb') + try: + zip = zipfile.ZipFile(zipfp) + leading = has_leading_dir(zip.namelist()) and flatten + for name in zip.namelist(): + data = zip.read(name) + fn = name + if leading: + fn = split_leading_dir(name)[1] + fn = os.path.join(location, fn) + dir = os.path.dirname(fn) + if not os.path.exists(dir): + os.makedirs(dir) + if fn.endswith('/') or fn.endswith('\\'): + # A directory + if not os.path.exists(fn): + os.makedirs(fn) + else: + fp = open(fn, 'wb') + try: + fp.write(data) + finally: + fp.close() + finally: + zipfp.close() + + +def untar_file(filename, location): + """Untar the file (tar file located at filename) to the destination + location + """ + if not os.path.exists(location): + os.makedirs(location) + if filename.lower().endswith('.gz') or filename.lower().endswith('.tgz'): + mode = 'r:gz' + elif (filename.lower().endswith('.bz2') + or filename.lower().endswith('.tbz')): + mode = 'r:bz2' + elif filename.lower().endswith('.tar'): + mode = 'r' + else: + mode = 'r:*' + tar = tarfile.open(filename, mode) + try: + leading = has_leading_dir([member.name for member in tar.getmembers()]) + for member in tar.getmembers(): + fn = member.name + if leading: + fn = split_leading_dir(fn)[1] + path = os.path.join(location, fn) + if member.isdir(): + if not os.path.exists(path): + os.makedirs(path) + else: + try: + fp = tar.extractfile(member) + except (KeyError, AttributeError), e: + # Some corrupt tar files seem to produce this + # (specifically bad symlinks) + continue + if not os.path.exists(os.path.dirname(path)): + os.makedirs(os.path.dirname(path)) + destfp = open(path, 'wb') + try: + shutil.copyfileobj(fp, destfp) + finally: + destfp.close() + fp.close() + finally: + tar.close() + + +def has_leading_dir(paths): + """Returns true if all the paths have the same leading path name + (i.e., everything is in one subdirectory in an archive)""" + common_prefix = None + for path in paths: + prefix, rest = split_leading_dir(path) + if not prefix: + return False + elif common_prefix is None: + common_prefix = prefix + elif prefix != common_prefix: + return False + return True + + +def split_leading_dir(path): + path = str(path) + path = path.lstrip('/').lstrip('\\') + if '/' in path and (('\\' in path and path.find('/') < path.find('\\')) + or '\\' not in path): + return path.split('/', 1) + elif '\\' in path: + return path.split('\\', 1) + else: + return path, '' -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:46 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:46 +0200 Subject: [Python-checkins] distutils2: Rename the "pypi" package to "index", refactor index.dist. Message-ID: tarek.ziade pushed 795db3d79331 to distutils2: http://hg.python.org/distutils2/rev/795db3d79331 changeset: 447:795db3d79331 user: Alexis Metaireau date: Thu Jul 15 14:01:17 2010 +0200 summary: Rename the "pypi" package to "index", refactor index.dist. files: docs/source/index.rst, docs/source/projects-index.client.rst, docs/source/projects-index.dist.rst, docs/source/projects-index.rst, docs/source/projects-index.simple.rst, docs/source/projects-index.xmlrpc.rst, docs/source/pypi.rst, src/distutils2/index/__init__.py, src/distutils2/index/base.py, src/distutils2/index/dist.py, src/distutils2/index/errors.py, src/distutils2/index/simple.py, src/distutils2/pypi/__init__.py, src/distutils2/pypi/dist.py, src/distutils2/pypi/errors.py, src/distutils2/pypi/simple.py, src/distutils2/tests/test_index_dist.py, src/distutils2/tests/test_index_simple.py, src/distutils2/tests/test_pypi_dist.py, src/distutils2/tests/test_pypi_simple.py diff --git a/docs/source/index.rst b/docs/source/index.rst --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -16,7 +16,7 @@ depgraph new_commands test_framework - pypi + projects-index version Indices and tables diff --git a/docs/source/pypi.rst b/docs/source/projects-index.client.rst rename from docs/source/pypi.rst rename to docs/source/projects-index.client.rst --- a/docs/source/pypi.rst +++ b/docs/source/projects-index.client.rst @@ -1,195 +1,24 @@ -========================================= -Tools to query PyPI: the PyPI package -========================================= +=============================== +High level API to Query indexes +=============================== -Distutils2 comes with a module (eg. `distutils2.pypi`) which contains -facilities to access the Python Package Index (named "pypi", and avalaible on -the url `http://pypi.python.org`. +Distutils2 provides a high level API to query indexes, search for releases and +distributions, no matters the underlying API you want to use. -There is two ways to retrieve data from pypi: using the *simple* API, and using -*XML-RPC*. The first one is in fact a set of HTML pages avalaible at -`http://pypi.python.org/simple/`, and the second one contains a set of XML-RPC -methods. In order to reduce the overload caused by running distant methods on -the pypi server (by using the XML-RPC methods), the best way to retrieve -informations is by using the simple API, when it contains the information you -need. - -Distutils2 provides two python modules to ease the work with those two APIs: -`distutils2.pypi.simple` and `distutils2.pypi.xmlrpc`. Both of them depends on -another python module: `distutils2.pypi.dist`. - - -Requesting information via the "simple" API `distutils2.pypi.simple` -==================================================================== - -`distutils2.pypi.simple` can process the Python Package Index and return and -download urls of distributions, for specific versions or latests, but it also -can process external html pages, with the goal to find *pypi unhosted* versions -of python distributions. - -You should use `distutils2.pypi.simple` for: - - * Search distributions by name and versions. - * Process pypi external pages. - * Download distributions by name and versions. - -And should not be used to: - - * Things that will end up in too long index processing (like "finding all - distributions with a specific version, no matters the name") +The aim of this module is to choose the best way to query the API, using the +less possible XML-RPC, and when possible the simple index. API ----- +=== -Here is a complete overview of the APIs of the SimpleIndex class. +The client comes with the common methods "find", "get" and "download", which +helps to query the servers, and returns. -.. autoclass:: distutils2.pypi.simple.SimpleIndex - :members: +:class:`distutils2.index.dist.ReleaseInfo`, and +:class:`distutils2.index.dist.ReleasesList` objects. -Usage Exemples ---------------- +XXX TODO Autoclass here. -To help you understand how using the `SimpleIndex` class, here are some basic -usages. +Exemples +========= -Request PyPI to get a specific distribution -++++++++++++++++++++++++++++++++++++++++++++ - -Supposing you want to scan the PyPI index to get a list of distributions for -the "foobar" project. You can use the "find" method for that:: - - >>> from distutils2.pypi import SimpleIndex - >>> client = SimpleIndex() - >>> client.find("foobar") - [, ] - -Note that you also can request the client about specific versions, using version -specifiers (described in `PEP 345 -`_):: - - >>> client.find("foobar < 1.2") - [, ] - -`find` returns a list of distributions, but you also can get the last -distribution (the more up to date) that fullfil your requirements, like this:: - - >>> client.get("foobar < 1.2") - - -Download distributions -+++++++++++++++++++++++ - -As it can get the urls of distributions provided by PyPI, the `SimpleIndex` -client also can download the distributions and put it for you in a temporary -destination:: - - >>> client.download("foobar") - /tmp/temp_dir/foobar-1.2.tar.gz - -You also can specify the directory you want to download to:: - - >>> client.download("foobar", "/path/to/my/dir") - /path/to/my/dir/foobar-1.2.tar.gz - -While downloading, the md5 of the archive will be checked, if not matches, it -will try another time, then if fails again, raise `MD5HashDoesNotMatchError`. - -Internally, that's not the SimpleIndex which download the distributions, but the -`PyPIDistribution` class. Please refer to this documentation for more details. - -Following PyPI external links -++++++++++++++++++++++++++++++ - -The default behavior for distutils2 is to *not* follow the links provided -by HTML pages in the "simple index", to find distributions related -downloads. - -It's possible to tell the PyPIClient to follow external links by setting the -`follow_externals` attribute, on instanciation or after:: - - >>> client = SimpleIndex(follow_externals=True) - -or :: - - >>> client = SimpleIndex() - >>> client.follow_externals = True - -Working with external indexes, and mirrors -+++++++++++++++++++++++++++++++++++++++++++ - -The default `SimpleIndex` behavior is to rely on the Python Package index stored -on PyPI (http://pypi.python.org/simple). - -As you can need to work with a local index, or private indexes, you can specify -it using the index_url parameter:: - - >>> client = SimpleIndex(index_url="file://filesystem/path/") - -or :: - - >>> client = SimpleIndex(index_url="http://some.specific.url/") - -You also can specify mirrors to fallback on in case the first index_url you -provided doesnt respond, or not correctly. The default behavior for -`SimpleIndex` is to use the list provided by Python.org DNS records, as -described in the :pep:`381` about mirroring infrastructure. - -If you don't want to rely on these, you could specify the list of mirrors you -want to try by specifying the `mirrors` attribute. It's a simple iterable:: - - >>> mirrors = ["http://first.mirror","http://second.mirror"] - >>> client = SimpleIndex(mirrors=mirrors) - - -Requesting informations via XML-RPC (`distutils2.pypi.XmlRpcIndex`) -========================================================================== - -The other method to request the Python package index, is using the XML-RPC -methods. Distutils2 provides a simple wrapper around `xmlrpclib -`_, that can return you -`PyPIDistribution` objects. - -:: - >>> from distutils2.pypi import XmlRpcIndex() - >>> client = XmlRpcIndex() - - -PyPI Distributions -================== - -Both `SimpleIndex` and `XmlRpcIndex` classes works with the classes provided -in the `pypi.dist` package. - -`PyPIDistribution` ------------------- - -`PyPIDistribution` is a simple class that defines the following attributes: - -:name: - The name of the package. `foobar` in our exemples here -:version: - The version of the package -:location: - If the files from the archive has been downloaded, here is the path where - you can find them. -:url: - The url of the distribution - -.. autoclass:: distutils2.pypi.dist.PyPIDistribution - :members: - -`PyPIDistributions` -------------------- - -The `dist` module also provides another class, to work with lists of -`PyPIDistribution` classes. It allow to filter results and is used as a -container of - -.. autoclass:: distutils2.pypi.dist.PyPIDistributions - :members: - -At a higher level -================= - -XXX : A description about a wraper around PyPI simple and XmlRpc Indexes -(PyPIIndex ?) diff --git a/docs/source/projects-index.dist.rst b/docs/source/projects-index.dist.rst new file mode 100644 --- /dev/null +++ b/docs/source/projects-index.dist.rst @@ -0,0 +1,87 @@ +================================================== +Representation of informations coming from indexes +================================================== + +Informations coming from indexes are represented by the classes present in the +`dist` module. + +.. note:: Keep in mind that each project (eg. FooBar) can have several + releases (eg. 1.1, 1.2, 1.3), and each of these releases can be + provided in multiple distributions (eg. a source distribution, + a binary one, etc). + +APIs +==== + +ReleaseInfo +------------ + +Each release have a project name, a project version and contain project +metadata. In addition, releases contain the distributions too. + +These informations are stored in :class:`distutils2.index.dist.ReleaseInfo` +objects. + +.. autoclass:: distutils2.index.dist.ReleaseInfo + :members: + +DistInfo +--------- + +:class:`distutils2.index.dist.DistInfo` is a simple class that contains +informations related to distributions. It's mainly about the URLs where those +distributions can be found. + +.. autoclass:: distutils2.index.dist.DistInfo + :members: + +ReleasesList +------------ + +The `dist` module also provides another class, to work with lists of +:class:`distutils.index.dist.ReleaseInfo` classes. It allow to filter +and order results. + +.. autoclass:: distutils2.index.dist.ReleasesList + :members: + +Exemple usages +=============== + +Build a list of releases, and order them +---------------------------------------- + +Assuming we have a list of releases:: + + >>> from distutils2.index.dist import ReleaseList, ReleaseInfo + >>> fb10 = ReleaseInfo("FooBar", "1.0") + >>> fb11 = ReleaseInfo("FooBar", "1.1") + >>> fb11a = ReleaseInfo("FooBar", "1.1a1") + >>> ReleasesList("FooBar", [fb11, fb11a, fb10]) + >>> releases.sort_releases() + >>> releases.get_versions() + ['1.1', '1.1a1', '1.0'] + >>> releases.add_release("1.2a1") + >>> releases.get_versions() + ['1.1', '1.1a1', '1.0', '1.2a1'] + >>> releases.sort_releases() + ['1.2a1', '1.1', '1.1a1', '1.0'] + >>> releases.sort_releases(prefer_final=True) + >>> releases.get_versions() + ['1.1', '1.0', '1.2a1', '1.1a1'] + + +Add distribution related informations to releases +------------------------------------------------- + +It's easy to add distribution informatons to releases:: + + >>> from distutils2.index.dist import ReleaseList, ReleaseInfo + >>> r = ReleaseInfo("FooBar", "1.0") + >>> r.add_distribution("sdist", url="http://example.org/foobar-1.0.tar.gz") + >>> r.dists + {'sdist': FooBar 1.0 sdist} + >>> r['sdist'].url + {'url': 'http://example.org/foobar-1.0.tar.gz', 'hashname': None, 'hashval': + None, 'is_external': True} + diff --git a/docs/source/pypi.rst b/docs/source/projects-index.rst copy from docs/source/pypi.rst copy to docs/source/projects-index.rst --- a/docs/source/pypi.rst +++ b/docs/source/projects-index.rst @@ -1,195 +1,28 @@ -========================================= -Tools to query PyPI: the PyPI package -========================================= +=================================== +Query Python Package Indexes (PyPI) +=================================== -Distutils2 comes with a module (eg. `distutils2.pypi`) which contains -facilities to access the Python Package Index (named "pypi", and avalaible on -the url `http://pypi.python.org`. +Distutils2 provides facilities to access python package informations stored in +indexes. The main Python Package Index is available at http://pypi.python.org. -There is two ways to retrieve data from pypi: using the *simple* API, and using -*XML-RPC*. The first one is in fact a set of HTML pages avalaible at +.. note:: The tools provided in distutils2 are not limited to query pypi, and + can be used for others indexes, if they respect the same interfaces. + +There is two ways to retrieve data from these indexes: using the *simple* API, +and using *XML-RPC*. The first one is a set of HTML pages avalaibles at `http://pypi.python.org/simple/`, and the second one contains a set of XML-RPC -methods. In order to reduce the overload caused by running distant methods on -the pypi server (by using the XML-RPC methods), the best way to retrieve -informations is by using the simple API, when it contains the information you -need. +methods. -Distutils2 provides two python modules to ease the work with those two APIs: -`distutils2.pypi.simple` and `distutils2.pypi.xmlrpc`. Both of them depends on -another python module: `distutils2.pypi.dist`. +If you dont care about which API to use, the best thing to do is to let +distutils2 decide this for you, by using :class:`distutils2.index.Client`. +Of course, you can rely too on :class:`distutils2.index.simple.Crawler` and +:class:`distutils.index.xmlrpc.Client` if you need to use these specific APIs. -Requesting information via the "simple" API `distutils2.pypi.simple` -==================================================================== +.. toctree:: + :maxdepth: 2 -`distutils2.pypi.simple` can process the Python Package Index and return and -download urls of distributions, for specific versions or latests, but it also -can process external html pages, with the goal to find *pypi unhosted* versions -of python distributions. - -You should use `distutils2.pypi.simple` for: - - * Search distributions by name and versions. - * Process pypi external pages. - * Download distributions by name and versions. - -And should not be used to: - - * Things that will end up in too long index processing (like "finding all - distributions with a specific version, no matters the name") - -API ----- - -Here is a complete overview of the APIs of the SimpleIndex class. - -.. autoclass:: distutils2.pypi.simple.SimpleIndex - :members: - -Usage Exemples ---------------- - -To help you understand how using the `SimpleIndex` class, here are some basic -usages. - -Request PyPI to get a specific distribution -++++++++++++++++++++++++++++++++++++++++++++ - -Supposing you want to scan the PyPI index to get a list of distributions for -the "foobar" project. You can use the "find" method for that:: - - >>> from distutils2.pypi import SimpleIndex - >>> client = SimpleIndex() - >>> client.find("foobar") - [, ] - -Note that you also can request the client about specific versions, using version -specifiers (described in `PEP 345 -`_):: - - >>> client.find("foobar < 1.2") - [, ] - -`find` returns a list of distributions, but you also can get the last -distribution (the more up to date) that fullfil your requirements, like this:: - - >>> client.get("foobar < 1.2") - - -Download distributions -+++++++++++++++++++++++ - -As it can get the urls of distributions provided by PyPI, the `SimpleIndex` -client also can download the distributions and put it for you in a temporary -destination:: - - >>> client.download("foobar") - /tmp/temp_dir/foobar-1.2.tar.gz - -You also can specify the directory you want to download to:: - - >>> client.download("foobar", "/path/to/my/dir") - /path/to/my/dir/foobar-1.2.tar.gz - -While downloading, the md5 of the archive will be checked, if not matches, it -will try another time, then if fails again, raise `MD5HashDoesNotMatchError`. - -Internally, that's not the SimpleIndex which download the distributions, but the -`PyPIDistribution` class. Please refer to this documentation for more details. - -Following PyPI external links -++++++++++++++++++++++++++++++ - -The default behavior for distutils2 is to *not* follow the links provided -by HTML pages in the "simple index", to find distributions related -downloads. - -It's possible to tell the PyPIClient to follow external links by setting the -`follow_externals` attribute, on instanciation or after:: - - >>> client = SimpleIndex(follow_externals=True) - -or :: - - >>> client = SimpleIndex() - >>> client.follow_externals = True - -Working with external indexes, and mirrors -+++++++++++++++++++++++++++++++++++++++++++ - -The default `SimpleIndex` behavior is to rely on the Python Package index stored -on PyPI (http://pypi.python.org/simple). - -As you can need to work with a local index, or private indexes, you can specify -it using the index_url parameter:: - - >>> client = SimpleIndex(index_url="file://filesystem/path/") - -or :: - - >>> client = SimpleIndex(index_url="http://some.specific.url/") - -You also can specify mirrors to fallback on in case the first index_url you -provided doesnt respond, or not correctly. The default behavior for -`SimpleIndex` is to use the list provided by Python.org DNS records, as -described in the :pep:`381` about mirroring infrastructure. - -If you don't want to rely on these, you could specify the list of mirrors you -want to try by specifying the `mirrors` attribute. It's a simple iterable:: - - >>> mirrors = ["http://first.mirror","http://second.mirror"] - >>> client = SimpleIndex(mirrors=mirrors) - - -Requesting informations via XML-RPC (`distutils2.pypi.XmlRpcIndex`) -========================================================================== - -The other method to request the Python package index, is using the XML-RPC -methods. Distutils2 provides a simple wrapper around `xmlrpclib -`_, that can return you -`PyPIDistribution` objects. - -:: - >>> from distutils2.pypi import XmlRpcIndex() - >>> client = XmlRpcIndex() - - -PyPI Distributions -================== - -Both `SimpleIndex` and `XmlRpcIndex` classes works with the classes provided -in the `pypi.dist` package. - -`PyPIDistribution` ------------------- - -`PyPIDistribution` is a simple class that defines the following attributes: - -:name: - The name of the package. `foobar` in our exemples here -:version: - The version of the package -:location: - If the files from the archive has been downloaded, here is the path where - you can find them. -:url: - The url of the distribution - -.. autoclass:: distutils2.pypi.dist.PyPIDistribution - :members: - -`PyPIDistributions` -------------------- - -The `dist` module also provides another class, to work with lists of -`PyPIDistribution` classes. It allow to filter results and is used as a -container of - -.. autoclass:: distutils2.pypi.dist.PyPIDistributions - :members: - -At a higher level -================= - -XXX : A description about a wraper around PyPI simple and XmlRpc Indexes -(PyPIIndex ?) + projects-index.client.rst + projects-index.dist.rst + projects-index.simple.rst + projects-index.xmlrpc.rst diff --git a/docs/source/projects-index.simple.rst b/docs/source/projects-index.simple.rst new file mode 100644 --- /dev/null +++ b/docs/source/projects-index.simple.rst @@ -0,0 +1,121 @@ +========================================= +Querying indexes via the simple index API +========================================= + +`distutils2.index.simple` can process Python Package Indexes, and provides +useful informations about distributions. It also can crawl local indexes, for +instance. + +You should use `distutils2.index.simple` for: + + * Search distributions by name and versions. + * Process index external pages. + * Download distributions by name and versions. + +And should not be used to: + + * Things that will end up in too long index processing (like "finding all + distributions with a specific version, no matters the name") + +API +--- + +.. autoclass:: distutils2.index.simple.Crawler + :members: + + +Usage Exemples +--------------- + +To help you understand how using the `SimpleIndexCrawler` class, here are some basic +usages. + +Request the simple index to get a specific distribution +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +Supposing you want to scan an index to get a list of distributions for +the "foobar" project. You can use the "find" method for that. +The find method will browse the project page, and return :class:`ReleaseInfo` +objects for each found link that rely on downloads. :: + + >>> from distutils2.index.simple import Crawler + >>> crawler = Crawler() + >>> crawler.find("FooBar") + [, ] + +Note that you also can request the client about specific versions, using version +specifiers (described in `PEP 345 +`_):: + + >>> client.find("FooBar < 1.2") + [, ] + +`find` returns a list of :class:`ReleaseInfo`, but you also can get the best +distribution that fullfil your requirements, using "get":: + + >>> client.get("FooBar < 1.2") + + +Download distributions ++++++++++++++++++++++++ + +As it can get the urls of distributions provided by PyPI, the `SimpleIndexCrawler` +client also can download the distributions and put it for you in a temporary +destination:: + + >>> client.download("foobar") + /tmp/temp_dir/foobar-1.2.tar.gz + +You also can specify the directory you want to download to:: + + >>> client.download("foobar", "/path/to/my/dir") + /path/to/my/dir/foobar-1.2.tar.gz + +While downloading, the md5 of the archive will be checked, if not matches, it +will try another time, then if fails again, raise `MD5HashDoesNotMatchError`. + +Internally, that's not the SimpleIndexCrawler which download the distributions, but the +`DistributionInfo` class. Please refer to this documentation for more details. + +Following PyPI external links +++++++++++++++++++++++++++++++ + +The default behavior for distutils2 is to *not* follow the links provided +by HTML pages in the "simple index", to find distributions related +downloads. + +It's possible to tell the PyPIClient to follow external links by setting the +`follow_externals` attribute, on instanciation or after:: + + >>> client = SimpleIndexCrawler(follow_externals=True) + +or :: + + >>> client = SimpleIndexCrawler() + >>> client.follow_externals = True + +Working with external indexes, and mirrors ++++++++++++++++++++++++++++++++++++++++++++ + +The default `SimpleIndexCrawler` behavior is to rely on the Python Package index stored +on PyPI (http://pypi.python.org/simple). + +As you can need to work with a local index, or private indexes, you can specify +it using the index_url parameter:: + + >>> client = SimpleIndexCrawler(index_url="file://filesystem/path/") + +or :: + + >>> client = SimpleIndexCrawler(index_url="http://some.specific.url/") + +You also can specify mirrors to fallback on in case the first index_url you +provided doesnt respond, or not correctly. The default behavior for +`SimpleIndexCrawler` is to use the list provided by Python.org DNS records, as +described in the :pep:`381` about mirroring infrastructure. + +If you don't want to rely on these, you could specify the list of mirrors you +want to try by specifying the `mirrors` attribute. It's a simple iterable:: + + >>> mirrors = ["http://first.mirror","http://second.mirror"] + >>> client = SimpleIndexCrawler(mirrors=mirrors) diff --git a/docs/source/projects-index.xmlrpc.rst b/docs/source/projects-index.xmlrpc.rst new file mode 100644 --- /dev/null +++ b/docs/source/projects-index.xmlrpc.rst @@ -0,0 +1,22 @@ +========================= +Query indexes via XML-RPC +========================= + +Indexes can be queried by using XML-RPC calls, and Distutils2 provides a simple +way to use this methods. + +The :class:`distutils2.xmlrpc.Client` have some specificities, that would be +described here. + +You should use XML-RPC for: + + * XXX TODO + +API +==== +:: + >>> from distutils2.index import XmlRpcClient() + >>> client = XmlRpcClient() + +Usage examples +=============== diff --git a/src/distutils2/pypi/__init__.py b/src/distutils2/index/__init__.py rename from src/distutils2/pypi/__init__.py rename to src/distutils2/index/__init__.py --- a/src/distutils2/pypi/__init__.py +++ b/src/distutils2/index/__init__.py @@ -1,6 +1,6 @@ -"""distutils2.pypi +"""distutils2.index -Package containing ways to interact with the PyPI APIs. +Package containing ways to interact with Index APIs. """ __all__ = ['simple', diff --git a/src/distutils2/index/base.py b/src/distutils2/index/base.py new file mode 100644 --- /dev/null +++ b/src/distutils2/index/base.py @@ -0,0 +1,88 @@ +from distutils2.version import VersionPredicate +from distutils2.index.errors import DistributionNotFound + + +class IndexClient(object): + """Base class containing common index client methods""" + + def _search_for_releases(self, requirements): + """To be redefined in child classes""" + return NotImplemented + + def find(self, requirements, prefer_final=None): + """Browse the PyPI to find distributions that fullfil the given + requirements. + + :param requirements: A project name and it's distribution, using + version specifiers, as described in PEP345. + :type requirements: You can pass either a version.VersionPredicate + or a string. + :param prefer_final: if the version is not mentioned in requirements, + and the last version is not a "final" one + (alpha, beta, etc.), pick up the last final + version. + """ + requirements = self._get_version_predicate(requirements) + prefer_final = self._get_prefer_final(prefer_final) + + # internally, rely on the "_search_for_release" method + dists = self._search_for_releases(requirements) + if dists: + dists = dists.filter(requirements) + dists.sort_releases(prefer_final=prefer_final) + return dists + + def get(self, requirements, prefer_final=None): + """Return only one release that fulfill the given requirements. + + :param requirements: A project name and it's distribution, using + version specifiers, as described in PEP345. + :type requirements: You can pass either a version.VersionPredicate + or a string. + :param prefer_final: if the version is not mentioned in requirements, + and the last version is not a "final" one + (alpha, beta, etc.), pick up the last final + version. + """ + predicate = self._get_version_predicate(requirements) + + # internally, rely on the "_get_release" method + dist = self._get_release(predicate, prefer_final=prefer_final) + if not dist: + raise DistributionNotFound(requirements) + return dist + + def download(self, requirements, temp_path=None, prefer_final=None, + prefer_source=True): + """Download the distribution, using the requirements. + + If more than one distribution match the requirements, use the last + version. + Download the distribution, and put it in the temp_path. If no temp_path + is given, creates and return one. + + Returns the complete absolute path to the downloaded archive. + + :param requirements: The same as the find attribute of `find`. + + You can specify prefer_final argument here. If not, the default + one will be used. + """ + return self.get(requirements, prefer_final)\ + .download(prefer_source=prefer_source, path=temp_path) + + def _get_version_predicate(self, requirements): + """Return a VersionPredicate object, from a string or an already + existing object. + """ + if isinstance(requirements, str): + requirements = VersionPredicate(requirements) + return requirements + + def _get_prefer_final(self, prefer_final=None): + """Return the prefer_final bit parameter or the specified one if + exists.""" + if prefer_final: + return prefer_final + else: + return self._prefer_final diff --git a/src/distutils2/pypi/dist.py b/src/distutils2/index/dist.py rename from src/distutils2/pypi/dist.py rename to src/distutils2/index/dist.py --- a/src/distutils2/pypi/dist.py +++ b/src/distutils2/index/dist.py @@ -1,98 +1,184 @@ -"""distutils2.pypi.dist +"""distutils2.index.dist -Provides the PyPIDistribution class thats represents a distribution retrieved -on PyPI. +Provides useful classes to represent the release and distributions retrieved +from indexes. + +A project can have several releases (=versions) and each release can have +several distributions (sdist, bdist). + +The release contains the metadata related informations (see PEP 384), and the +distributions contains download related informations. + """ import re +import tempfile +import urllib import urlparse -import urllib -import tempfile -from operator import attrgetter try: import hashlib except ImportError: from distutils2._backport import hashlib +from distutils2.index.errors import (HashDoesNotMatch, UnsupportedHashName, + CantParseArchiveName) from distutils2.version import suggest_normalized_version, NormalizedVersion -from distutils2.pypi.errors import HashDoesNotMatch, UnsupportedHashName +from distutils2.metadata import DistributionMetadata EXTENSIONS = ".tar.gz .tar.bz2 .tar .zip .tgz .egg".split() MD5_HASH = re.compile(r'^.*#md5=([a-f0-9]+)$') +DIST_TYPES = ['bdist', 'sdist'] -class PyPIDistribution(object): - """Represents a distribution retrieved from PyPI. +class ReleaseInfo(object): + """Represent a release of a project (a project with a specific version). + The release contain the metadata informations related to this specific + version, and is also a container for distribution related informations. - This is a simple container for various attributes as name, version, - downloaded_location, url etc. - - The PyPIDistribution class is used by the pypi.*Index class to return - information about distributions. + See the DistInfo class for more information about distributions. """ - @classmethod - def from_url(cls, url, probable_dist_name=None, is_external=True): - """Build a Distribution from a url archive (egg or zip or tgz). - - :param url: complete url of the distribution - :param probable_dist_name: A probable name of the distribution. - :param is_external: Tell if the url commes from an index or from - an external URL. + def __init__(self, name, version, metadata=None, hidden=False, **kwargs): """ - # if the url contains a md5 hash, get it. - md5_hash = None - match = MD5_HASH.match(url) - if match is not None: - md5_hash = match.group(1) - # remove the hash - url = url.replace("#md5=%s" % md5_hash, "") - - # parse the archive name to find dist name and version - archive_name = urlparse.urlparse(url)[2].split('/')[-1] - extension_matched = False - # remove the extension from the name - for ext in EXTENSIONS: - if archive_name.endswith(ext): - archive_name = archive_name[:-len(ext)] - extension_matched = True - - name, version = split_archive_name(archive_name) - if extension_matched is True: - return PyPIDistribution(name, version, url=url, url_hashname="md5", - url_hashval=md5_hash, - url_is_external=is_external) - - def __init__(self, name, version, type=None, url=None, url_hashname=None, - url_hashval=None, url_is_external=True): - """Create a new instance of PyPIDistribution. - :param name: the name of the distribution :param version: the version of the distribution - :param type: the type of the dist (eg. source, bin-*, etc.) - :param url: URL where we found this distribution - :param url_hashname: the name of the hash we want to use. Refer to the - hashlib.new documentation for more information. - :param url_hashval: the hash value. - :param url_is_external: we need to know if the provided url comes from an - index browsing, or from an external resource. - + :param metadata: the metadata fields of the release. + :type metadata: dict + :param kwargs: optional arguments for a new distribution. """ self.name = name self.version = NormalizedVersion(version) - self.type = type + self.metadata = DistributionMetadata() # XXX from_dict=metadata) + self.dists = {} + self.hidden = hidden + + if 'dist_type' in kwargs: + dist_type = kwargs.pop('dist_type') + self.add_distribution(dist_type, **kwargs) + + @property + def is_final(self): + """proxy to version.is_final""" + return self.version.is_final + + def add_distribution(self, dist_type='sdist', **params): + """Add distribution informations to this release. + If distribution information is already set for this distribution type, + add the given url paths to the distribution. This can be useful while + some of them fails to download. + + :param dist_type: the distribution type (eg. "sdist", "bdist", etc.) + :param params: the fields to be passed to the distribution object + (see the :class:DistInfo constructor). + """ + if dist_type not in DIST_TYPES: + raise ValueError(dist_type) + if dist_type in self.dists: + self.dists[dist_type].add_url(**params) + else: + self.dists[dist_type] = DistInfo(self, dist_type, **params) + + def get_distribution(self, dist_type=None, prefer_source=True): + """Return a distribution. + + If dist_type is set, find first for this distribution type, and just + act as an alias of __get_item__. + + If prefer_source is True, search first for source distribution, and if + not return one existing distribution. + """ + if len(self.dists) == 0: + raise LookupError() + if dist_type: + return self[dist_type] + if prefer_source: + if "sdist" in self.dists: + dist = self["sdist"] + else: + dist = self.dists.values()[0] + return dist + + def download(self, temp_path=None, prefer_source=True): + """Download the distribution, using the requirements. + + If more than one distribution match the requirements, use the last + version. + Download the distribution, and put it in the temp_path. If no temp_path + is given, creates and return one. + + Returns the complete absolute path to the downloaded archive. + """ + return self.get_distribution(prefer_source=prefer_source)\ + .download(path=temp_path) + + def __getitem__(self, item): + """distributions are available using release["sdist"]""" + return self.dists[item] + + def _check_is_comparable(self, other): + if not isinstance(other, ReleaseInfo): + raise TypeError("cannot compare %s and %s" + % (type(self).__name__, type(other).__name__)) + elif self.name != other.name: + raise TypeError("cannot compare %s and %s" + % (self.name, other.name)) + + def __eq__(self, other): + self._check_is_comparable(other) + return self.version == other.version + + def __lt__(self, other): + self._check_is_comparable(other) + return self.version < other.version + + def __ne__(self, other): + return not self.__eq__(other) + + def __gt__(self, other): + return not (self.__lt__(other) or self.__eq__(other)) + + def __le__(self, other): + return self.__eq__(other) or self.__lt__(other) + + def __ge__(self, other): + return self.__eq__(other) or self.__gt__(other) + + # See http://docs.python.org/reference/datamodel#object.__hash__ + __hash__ = object.__hash__ + + +class DistInfo(object): + """Represents a distribution retrieved from an index (sdist, bdist, ...) + """ + + def __init__(self, release, dist_type=None, url=None, hashname=None, + hashval=None, is_external=True): + """Create a new instance of DistInfo. + + :param release: a DistInfo class is relative to a release. + :param dist_type: the type of the dist (eg. source, bin-*, etc.) + :param url: URL where we found this distribution + :param hashname: the name of the hash we want to use. Refer to the + hashlib.new documentation for more information. + :param hashval: the hash value. + :param is_external: we need to know if the provided url comes from + an index browsing, or from an external resource. + + """ + self.release = release + self.dist_type = dist_type # set the downloaded path to None by default. The goal here # is to not download distributions multiple times self.downloaded_location = None - # We store urls in dict, because we need to have a bit more informations + # We store urls in dict, because we need to have a bit more infos # than the simple URL. It will be used later to find the good url to # use. - # We have two _url* attributes: _url and _urls. _urls contains a list of - # dict for the different urls, and _url contains the choosen url, in + # We have two _url* attributes: _url and urls. urls contains a list + # of dict for the different urls, and _url contains the choosen url, in # order to dont make the selection process multiple times. - self._urls = [] + self.urls = [] self._url = None - self.add_url(url, url_hashname, url_hashval, url_is_external) + self.add_url(url, hashname, hashval, is_external) def add_url(self, url, hashname=None, hashval=None, is_external=True): """Add a new url to the list of urls""" @@ -101,15 +187,15 @@ hashlib.new(hashname) except ValueError: raise UnsupportedHashName(hashname) - - self._urls.append({ - 'url': url, - 'hashname': hashname, - 'hashval': hashval, - 'is_external': is_external, - }) - # reset the url selection process - self._url = None + if not url in [u['url'] for u in self.urls]: + self.urls.append({ + 'url': url, + 'hashname': hashname, + 'hashval': hashval, + 'is_external': is_external, + }) + # reset the url selection process + self._url = None @property def url(self): @@ -118,24 +204,19 @@ # If there is more than one internal or external, return the first # one. if self._url is None: - if len(self._urls) > 1: - internals_urls = [u for u in self._urls \ + if len(self.urls) > 1: + internals_urls = [u for u in self.urls \ if u['is_external'] == False] if len(internals_urls) >= 1: self._url = internals_urls[0] if self._url is None: - self._url = self._urls[0] + self._url = self.urls[0] return self._url @property def is_source(self): """return if the distribution is a source one or not""" - return self.type == 'source' - - @property - def is_final(self): - """proxy to version.is_final""" - return self.version.is_final + return self.dist_type == 'sdist' def download(self, path=None): """Download the distribution to a path, and return it. @@ -169,113 +250,140 @@ % (hashval.hexdigest(), expected_hashval)) def __repr__(self): - return "%s %s %s %s" \ - % (self.__class__.__name__, self.name, self.version, - self.type or "") + return "%s %s %s" % ( + self.release.name, self.release.version, self.dist_type or "") - def _check_is_comparable(self, other): - if not isinstance(other, PyPIDistribution): - raise TypeError("cannot compare %s and %s" - % (type(self).__name__, type(other).__name__)) - elif self.name != other.name: - raise TypeError("cannot compare %s and %s" - % (self.name, other.name)) - def __eq__(self, other): - self._check_is_comparable(other) - return self.version == other.version +class ReleasesList(list): + """A container of Release. - def __lt__(self, other): - self._check_is_comparable(other) - return self.version < other.version - - def __ne__(self, other): - return not self.__eq__(other) - - def __gt__(self, other): - return not (self.__lt__(other) or self.__eq__(other)) - - def __le__(self, other): - return self.__eq__(other) or self.__lt__(other) - - def __ge__(self, other): - return self.__eq__(other) or self.__gt__(other) - - # See http://docs.python.org/reference/datamodel#object.__hash__ - __hash__ = object.__hash__ - - -class PyPIDistributions(list): - """A container of PyPIDistribution objects. - - Contains methods and facilities to sort and filter distributions. + Provides useful methods and facilities to sort and filter releases. """ - def __init__(self, list=[]): - # To disable the ability to pass lists on instanciation - super(PyPIDistributions, self).__init__() + def __init__(self, name, list=[], contains_hidden=False): + super(ReleasesList, self).__init__() for item in list: self.append(item) + self.name = name + self.contains_hidden = contains_hidden + + def filter(self, predicate): + """Filter and return a subset of releases matching the given predicate. + """ + return ReleasesList(self.name, [release for release in self + if release.name == predicate.name + and predicate.match(release.version)]) - def filter(self, predicate): - """Filter the distributions and return a subset of distributions that - match the given predicate + def get_last(self, predicate, prefer_final=None): + """Return the "last" release, that satisfy the given predicates. + + "last" is defined by the version number of the releases, you also could + set prefer_final parameter to True or False to change the order results """ - return PyPIDistributions( - [dist for dist in self if dist.name == predicate.name and - predicate.match(dist.version)]) + releases = self.filter(predicate) + releases.sort_releases(prefer_final, reverse=True) + return releases[0] - def get_last(self, predicate, prefer_source=None, prefer_final=None): - """Return the most up to date version, that satisfy the given - predicate + def add_release(self, version=None, dist_type='sdist', release=None, + **dist_args): + """Add a release to the list. + + The release can be passed in the `release` parameter, and in this case, + it will be crawled to extract the useful informations if necessary, or + the release informations can be directly passed in the `version` and + `dist_type` arguments. + + Other keywords arguments can be provided, and will be forwarded to the + distribution creation (eg. the arguments of the DistInfo constructor). """ - distributions = self.filter(predicate) - distributions.sort_distributions(prefer_source, prefer_final, reverse=True) - return distributions[0] + if release: + if release.name != self.name: + raise ValueError(release.name) + version = '%s' % release.version + for dist in release.dists.values(): + for url in dist.urls: + self.add_release(version, dist.dist_type, **url) + else: + matches = [r for r in self if '%s' % r.version == version + and r.name == self.name] + if not matches: + release = ReleaseInfo(self.name, version) + self.append(release) + else: + release = matches[0] - def get_same_name_and_version(self): - """Return lists of PyPIDistribution objects that refer to the same - name and version number. This do not consider the type (source, binary, - etc.)""" - processed = [] - duplicates = [] - for dist in self: - if (dist.name, dist.version) not in processed: - processed.append((dist.name, dist.version)) - found_duplicates = [d for d in self if d.name == dist.name and - d.version == dist.version] - if len(found_duplicates) > 1: - duplicates.append(found_duplicates) - return duplicates + release.add_distribution(dist_type=dist_type, **dist_args) - def append(self, o): - """Append a new distribution to the list. + def sort_releases(self, prefer_final=False, reverse=True, *args, **kwargs): + """Sort the results with the given properties. - If a distribution with the same name and version exists, just grab the - URL informations and add a new new url for the existing one. + The `prefer_final` argument can be used to specify if final + distributions (eg. not dev, bet or alpha) would be prefered or not. + + Results can be inverted by using `reverse`. + + Any other parameter provided will be forwarded to the sorted call. You + cannot redefine the key argument of "sorted" here, as it is used + internally to sort the releases. """ - similar_dists = [d for d in self if d.name == o.name and - d.version == o.version and d.type == o.type] - if len(similar_dists) > 0: - dist = similar_dists[0] - dist.add_url(**o.url) - else: - super(PyPIDistributions, self).append(o) - - def sort_distributions(self, prefer_source=True, prefer_final=False, - reverse=True, *args, **kwargs): - """order the results with the given properties""" sort_by = [] if prefer_final: sort_by.append("is_final") sort_by.append("version") - if prefer_source: - sort_by.append("is_source") - - super(PyPIDistributions, self).sort( + super(ReleasesList, self).sort( key=lambda i: [getattr(i, arg) for arg in sort_by], reverse=reverse, *args, **kwargs) + + def get_release(self, version): + """Return a release from it's version. + """ + matches = [r for r in self if "%s" % r.version == version] + if len(matches) != 1: + raise KeyError(version) + return matches[0] + + def get_versions(self): + """Return a list of releases versions contained""" + return ["%s" % r.version for r in self] + + +def get_infos_from_url(url, probable_dist_name=None, is_external=True): + """Get useful informations from an URL. + + Return a dict of (name, version, url, hashtype, hash, is_external) + + :param url: complete url of the distribution + :param probable_dist_name: A probable name of the project. + :param is_external: Tell if the url commes from an index or from + an external URL. + """ + # if the url contains a md5 hash, get it. + md5_hash = None + match = MD5_HASH.match(url) + if match is not None: + md5_hash = match.group(1) + # remove the hash + url = url.replace("#md5=%s" % md5_hash, "") + + # parse the archive name to find dist name and version + archive_name = urlparse.urlparse(url)[2].split('/')[-1] + extension_matched = False + # remove the extension from the name + for ext in EXTENSIONS: + if archive_name.endswith(ext): + archive_name = archive_name[:-len(ext)] + extension_matched = True + + name, version = split_archive_name(archive_name) + if extension_matched is True: + return {'name': name, + 'version': version, + 'url': url, + 'hashname': "md5", + 'hashval': md5_hash, + 'is_external': is_external, + 'dist_type': 'sdist'} def split_archive_name(archive_name, probable_name=None): diff --git a/src/distutils2/pypi/errors.py b/src/distutils2/index/errors.py rename from src/distutils2/pypi/errors.py rename to src/distutils2/index/errors.py --- a/src/distutils2/pypi/errors.py +++ b/src/distutils2/index/errors.py @@ -5,19 +5,19 @@ from distutils2.errors import DistutilsError -class PyPIError(DistutilsError): - """The base class for errors of the pypi python package.""" +class IndexError(DistutilsError): + """The base class for errors of the index python package.""" -class DistributionNotFound(PyPIError): +class DistributionNotFound(IndexError): """No distribution match the given requirements.""" -class CantParseArchiveName(PyPIError): +class CantParseArchiveName(IndexError): """An archive name can't be parsed to find distribution name and version""" -class DownloadError(PyPIError): +class DownloadError(IndexError): """An error has occurs while downloading""" @@ -25,9 +25,9 @@ """Compared hashes does not match""" -class UnsupportedHashName(PyPIError): +class UnsupportedHashName(IndexError): """A unsupported hashname has been used""" -class UnableToDownload(PyPIError): +class UnableToDownload(IndexError): """All mirrors have been tried, without success""" diff --git a/src/distutils2/pypi/simple.py b/src/distutils2/index/simple.py rename from src/distutils2/pypi/simple.py rename to src/distutils2/index/simple.py --- a/src/distutils2/pypi/simple.py +++ b/src/distutils2/index/simple.py @@ -1,6 +1,6 @@ -"""pypi.simple +"""index.simple -Contains the class "SimpleIndex", a simple spider to find and retrieve +Contains the class "SimpleIndexCrawler", a simple spider to find and retrieve distributions on the Python Package Index, using it's "simple" API, avalaible at http://pypi.python.org/simple/ """ @@ -12,16 +12,16 @@ import urllib2 import urlparse -from distutils2.version import VersionPredicate -from distutils2.pypi.dist import (PyPIDistribution, PyPIDistributions, - EXTENSIONS) -from distutils2.pypi.errors import (PyPIError, DistributionNotFound, - DownloadError, UnableToDownload) +from distutils2.index.base import IndexClient +from distutils2.index.dist import (ReleasesList, EXTENSIONS, + get_infos_from_url) +from distutils2.index.errors import (IndexError, DownloadError, + UnableToDownload) from distutils2 import __version__ as __distutils2_version__ # -- Constants ----------------------------------------------- -PYPI_DEFAULT_INDEX_URL = "http://pypi.python.org/simple/" -PYPI_DEFAULT_MIRROR_URL = "mirrors.pypi.python.org" +DEFAULT_INDEX_URL = "http://pypi.python.org/simple/" +DEFAULT_MIRROR_URL = "mirrors.pypi.python.org" DEFAULT_HOSTS = ("*",) SOCKET_TIMEOUT = 15 USER_AGENT = "Python-urllib/%s distutils2/%s" % ( @@ -58,8 +58,8 @@ return _socket_timeout -class SimpleIndex(object): - """Provides useful tools to request the Python Package Index simple API +class Crawler(IndexClient): + """Provides useful tools to request the Python Package Index simple API. :param index_url: the url of the simple index to search on. :param follow_externals: tell if following external links is needed or @@ -69,8 +69,6 @@ hosts. :param follow_externals: tell if following external links is needed or not. Default is False. - :param prefer_source: if there is binary and source distributions, the - source prevails. :param prefer_final: if the version is not mentioned, and the last version is not a "final" one (alpha, beta, etc.), pick up the last final version. @@ -81,10 +79,10 @@ :param timeout: time in seconds to consider a url has timeouted. """ - def __init__(self, index_url=PYPI_DEFAULT_INDEX_URL, hosts=DEFAULT_HOSTS, - follow_externals=False, prefer_source=True, - prefer_final=False, mirrors_url=PYPI_DEFAULT_MIRROR_URL, - mirrors=None, timeout=SOCKET_TIMEOUT): + def __init__(self, index_url=DEFAULT_INDEX_URL, hosts=DEFAULT_HOSTS, + follow_externals=False, prefer_final=False, + mirrors_url=DEFAULT_MIRROR_URL, mirrors=None, + timeout=SOCKET_TIMEOUT): self.follow_externals = follow_externals if not index_url.endswith("/"): @@ -99,7 +97,6 @@ self._index_urls.extend(mirrors) self._current_index_url = 0 self._timeout = timeout - self._prefer_source = prefer_source self._prefer_final = prefer_final # create a regexp to match all given hosts @@ -109,86 +106,26 @@ # scanning them multple time (eg. if there is multiple pages pointing # on one) self._processed_urls = [] - self._distributions = {} - - def find(self, requirements, prefer_source=None, prefer_final=None): - """Browse the PyPI to find distributions that fullfil the given - requirements. - - :param requirements: A project name and it's distribution, using - version specifiers, as described in PEP345. - :type requirements: You can pass either a version.VersionPredicate - or a string. - :param prefer_source: if there is binary and source distributions, the - source prevails. - :param prefer_final: if the version is not mentioned, and the last - version is not a "final" one (alpha, beta, etc.), - pick up the last final version. - """ - requirements = self._get_version_predicate(requirements) - if prefer_source is None: - prefer_source = self._prefer_source - if prefer_final is None: - prefer_final = self._prefer_final - - # process the index for this project - self._process_pypi_page(requirements.name) - - # filter with requirements and return the results - if requirements.name in self._distributions: - dists = self._distributions[requirements.name].filter(requirements) - dists.sort_distributions(prefer_source=prefer_source, - prefer_final=prefer_final) - else: - dists = [] - - return dists - - def get(self, requirements, *args, **kwargs): - """Browse the PyPI index to find distributions that fullfil the - given requirements, and return the most recent one. - - You can specify prefer_final and prefer_source arguments here. - If not, the default one will be used. - """ - predicate = self._get_version_predicate(requirements) - dists = self.find(predicate, *args, **kwargs) - - if len(dists) == 0: - raise DistributionNotFound(requirements) - - return dists.get_last(predicate) - - def download(self, requirements, temp_path=None, *args, **kwargs): - """Download the distribution, using the requirements. - - If more than one distribution match the requirements, use the last - version. - Download the distribution, and put it in the temp_path. If no temp_path - is given, creates and return one. - - Returns the complete absolute path to the downloaded archive. - - :param requirements: The same as the find attribute of `find`. - - You can specify prefer_final and prefer_source arguments here. - If not, the default one will be used. - """ - return self.get(requirements, *args, **kwargs)\ - .download(path=temp_path) - - def _get_version_predicate(self, requirements): - """Return a VersionPredicate object, from a string or an already - existing object. - """ - if isinstance(requirements, str): - requirements = VersionPredicate(requirements) - return requirements - + self._releases = {} + @property def index_url(self): return self._index_urls[self._current_index_url] + def _search_for_releases(self, requirements): + """Search for distributions and return a ReleaseList object containing + the results + """ + # process the index page for the project name, searching for + # distributions. + self._process_index_page(requirements.name) + return self._releases.setdefault(requirements.name, + ReleasesList(requirements.name)) + + def _get_release(self, requirements, prefer_final): + """Return only one release that fulfill the given requirements""" + return self.find(requirements, prefer_final).get_last(requirements) + def _switch_to_next_mirror(self): """Switch to the next mirror (eg. point self.index_url to the next url. @@ -228,18 +165,33 @@ return True return False - def _register_dist(self, dist): - """Register a distribution as a part of fetched distributions for - SimpleIndex. + def _register_release(self, release=None, release_info={}): + """Register a new release. - Return the PyPIDistributions object for the specified project name + Both a release or a dict of release_info can be provided, the prefered + way (eg. the quicker) is the dict one. + + Return the list of existing releases for the given project. """ - # Internally, check if a entry exists with the project name, if not, - # create a new one, and if exists, add the dist to the pool. - if not dist.name in self._distributions: - self._distributions[dist.name] = PyPIDistributions() - self._distributions[dist.name].append(dist) - return self._distributions[dist.name] + # Check if the project already has a list of releases (refering to + # the project name). If not, create a new release list. + # Then, add the release to the list. + if release: + name = release.name + else: + name = release_info['name'] + if not name in self._releases: + self._releases[name] = ReleasesList(name) + + if release: + self._releases[name].add_release(release=release) + else: + name = release_info.pop('name') + version = release_info.pop('version') + dist_type = release_info.pop('dist_type') + self._releases[name].add_release(version, dist_type, + **release_info) + return self._releases[name] def _process_url(self, url, project_name=None, follow_links=True): """Process an url and search for distributions packages. @@ -264,9 +216,9 @@ if self._is_distribution(link) or is_download: self._processed_urls.append(link) # it's a distribution, so create a dist object - dist = PyPIDistribution.from_url(link, project_name, - is_external=not self.index_url in url) - self._register_dist(dist) + infos = get_infos_from_url(link, project_name, + is_external=not self.index_url in url) + self._register_release(release_info=infos) else: if self._is_browsable(link) and follow_links: self._process_url(link, project_name, @@ -307,7 +259,7 @@ if self._is_browsable(url): yield (url, False) - def _process_pypi_page(self, name): + def _process_index_page(self, name): """Find and process a PyPI page for the given project name. :param name: the name of the project to find the page @@ -320,8 +272,8 @@ # if an error occurs, try with the next index_url # (provided by the mirrors) self._switch_to_next_mirror() - self._distributions.clear() - self._process_pypi_page(name) + self._releases.clear() + self._process_index_page(name) @socket_timeout() def _open_url(self, url): @@ -366,7 +318,7 @@ return fp except (ValueError, httplib.InvalidURL), v: msg = ' '.join([str(arg) for arg in v.args]) - raise PyPIError('%s %s' % (url, msg)) + raise IndexError('%s %s' % (url, msg)) except urllib2.HTTPError, v: return v except urllib2.URLError, v: diff --git a/src/distutils2/tests/test_pypi_dist.py b/src/distutils2/tests/test_index_dist.py rename from src/distutils2/tests/test_pypi_dist.py rename to src/distutils2/tests/test_index_dist.py --- a/src/distutils2/tests/test_pypi_dist.py +++ b/src/distutils2/tests/test_index_dist.py @@ -1,76 +1,93 @@ -"""Tests for the distutils2.pypi.dist module.""" +"""Tests for the distutils2.index.dist module.""" import os -import shutil -import tempfile from distutils2.tests.pypi_server import use_pypi_server from distutils2.tests import run_unittest from distutils2.tests.support import unittest, TempdirManager from distutils2.version import VersionPredicate -from distutils2.pypi.errors import HashDoesNotMatch, UnsupportedHashName -from distutils2.pypi.dist import (PyPIDistribution as Dist, - PyPIDistributions as Dists, - split_archive_name) +from distutils2.index.errors import HashDoesNotMatch, UnsupportedHashName +from distutils2.index.dist import (ReleaseInfo, ReleasesList, DistInfo, + split_archive_name, get_infos_from_url) -class TestPyPIDistribution(TempdirManager, - unittest.TestCase): - """Tests the pypi.dist.PyPIDistribution class""" +def Dist(*args, **kwargs): + # DistInfo takes a release as a first parameter, avoid this in tests. + return DistInfo(None, *args, **kwargs) + + +class TestReleaseInfo(unittest.TestCase): def test_instanciation(self): - # Test the Distribution class provides us the good attributes when + # Test the DistInfo class provides us the good attributes when # given on construction - dist = Dist("FooBar", "1.1") - self.assertEqual("FooBar", dist.name) - self.assertEqual("1.1", "%s" % dist.version) + release = ReleaseInfo("FooBar", "1.1") + self.assertEqual("FooBar", release.name) + self.assertEqual("1.1", "%s" % release.version) - def test_create_from_url(self): - # Test that the Distribution object can be built from a single URL + def test_add_dist(self): + # empty distribution type should assume "sdist" + release = ReleaseInfo("FooBar", "1.1") + release.add_distribution(url="http://example.org/") + # should not fail + release['sdist'] + + def test_get_unknown_distribution(self): + # should raise a KeyError + pass + + def test_get_infos_from_url(self): + # Test that the the URLs are parsed the right way url_list = { 'FooBar-1.1.0.tar.gz': { 'name': 'foobar', # lowercase the name - 'version': '1.1', + 'version': '1.1.0', }, 'Foo-Bar-1.1.0.zip': { 'name': 'foo-bar', # keep the dash - 'version': '1.1', + 'version': '1.1.0', }, 'foobar-1.1b2.tar.gz#md5=123123123123123': { 'name': 'foobar', 'version': '1.1b2', - 'url': { - 'url': 'http://test.tld/foobar-1.1b2.tar.gz', # no hash - 'hashval': '123123123123123', - 'hashname': 'md5', - } + 'url': 'http://example.org/foobar-1.1b2.tar.gz', # no hash + 'hashval': '123123123123123', + 'hashname': 'md5', }, 'foobar-1.1-rc2.tar.gz': { # use suggested name 'name': 'foobar', 'version': '1.1c2', - 'url': { - 'url': 'http://test.tld/foobar-1.1-rc2.tar.gz', - } + 'url': 'http://example.org/foobar-1.1-rc2.tar.gz', } } for url, attributes in url_list.items(): - dist = Dist.from_url("http://test.tld/" + url) - for attribute, value in attributes.items(): - if isinstance(value, dict): - mylist = getattr(dist, attribute) - for val in value.keys(): - self.assertEqual(value[val], mylist[val]) + # for each url + infos = get_infos_from_url("http://example.org/" + url) + for attribute, expected in attributes.items(): + got = infos.get(attribute) + if attribute == "version": + self.assertEqual("%s" % got, expected) else: - if attribute == "version": - self.assertEqual("%s" % getattr(dist, "version"), value) - else: - self.assertEqual(getattr(dist, attribute), value) + self.assertEqual(got, expected) + + def test_split_archive_name(self): + # Test we can split the archive names + names = { + 'foo-bar-baz-1.0-rc2': ('foo-bar-baz', '1.0c2'), + 'foo-bar-baz-1.0': ('foo-bar-baz', '1.0'), + 'foobarbaz-1.0': ('foobarbaz', '1.0'), + } + for name, results in names.items(): + self.assertEqual(results, split_archive_name(name)) + + +class TestDistInfo(TempdirManager, unittest.TestCase): def test_get_url(self): # Test that the url property works well - d = Dist("foobar", "1.1", url="test_url") + d = Dist(url="test_url") self.assertDictEqual(d.url, { "url": "test_url", "is_external": True, @@ -87,13 +104,13 @@ "hashname": None, "hashval": None, }) - self.assertEqual(2, len(d._urls)) + self.assertEqual(2, len(d.urls)) def test_comparaison(self): - # Test that we can compare PyPIDistributions - foo1 = Dist("foo", "1.0") - foo2 = Dist("foo", "2.0") - bar = Dist("bar", "2.0") + # Test that we can compare DistInfoributionInfoList + foo1 = ReleaseInfo("foo", "1.0") + foo2 = ReleaseInfo("foo", "2.0") + bar = ReleaseInfo("bar", "2.0") # assert we use the version to compare self.assertTrue(foo1 < foo2) self.assertFalse(foo1 > foo2) @@ -102,16 +119,6 @@ # assert we can't compare dists with different names self.assertRaises(TypeError, foo1.__eq__, bar) - def test_split_archive_name(self): - # Test we can split the archive names - names = { - 'foo-bar-baz-1.0-rc2': ('foo-bar-baz', '1.0c2'), - 'foo-bar-baz-1.0': ('foo-bar-baz', '1.0'), - 'foobarbaz-1.0': ('foobarbaz', '1.0'), - } - for name, results in names.items(): - self.assertEqual(results, split_archive_name(name)) - @use_pypi_server("downloads_with_md5") def test_download(self, server): # Download is possible, and the md5 is checked if given @@ -120,19 +127,18 @@ url = "%s/simple/foobar/foobar-0.1.tar.gz" % server.full_address # check md5 if given - dist = Dist("FooBar", "0.1", url=url, - url_hashname="md5", url_hashval="d41d8cd98f00b204e9800998ecf8427e") + dist = Dist(url=url, hashname="md5", + hashval="d41d8cd98f00b204e9800998ecf8427e") add_to_tmpdirs(dist.download()) # a wrong md5 fails - dist2 = Dist("FooBar", "0.1", url=url, - url_hashname="md5", url_hashval="wrongmd5") + dist2 = Dist(url=url, hashname="md5", hashval="wrongmd5") self.assertRaises(HashDoesNotMatch, dist2.download) add_to_tmpdirs(dist2.downloaded_location) # we can omit the md5 hash - dist3 = Dist("FooBar", "0.1", url=url) + dist3 = Dist(url=url) add_to_tmpdirs(dist3.download()) # and specify a temporary location @@ -141,106 +147,104 @@ dist3.download(path=path1) # and for a new one path2_base = self.mkdtemp() - dist4 = Dist("FooBar", "0.1", url=url) + dist4 = Dist(url=url) path2 = dist4.download(path=path2_base) self.assertTrue(path2_base in path2) def test_hashname(self): # Invalid hashnames raises an exception on assignation - Dist("FooBar", "0.1", url_hashname="md5", url_hashval="value") + Dist(hashname="md5", hashval="value") - self.assertRaises(UnsupportedHashName, Dist, "FooBar", "0.1", - url_hashname="invalid_hashname", url_hashval="value") + self.assertRaises(UnsupportedHashName, Dist, + hashname="invalid_hashname", + hashval="value") -class TestPyPIDistributions(unittest.TestCase): +class TestReleasesList(unittest.TestCase): def test_filter(self): # Test we filter the distributions the right way, using version # predicate match method - dists = Dists(( - Dist("FooBar", "1.1"), - Dist("FooBar", "1.1.1"), - Dist("FooBar", "1.2"), - Dist("FooBar", "1.2.1"), + releases = ReleasesList('FooBar', ( + ReleaseInfo("FooBar", "1.1"), + ReleaseInfo("FooBar", "1.1.1"), + ReleaseInfo("FooBar", "1.2"), + ReleaseInfo("FooBar", "1.2.1"), )) - filtered = dists.filter(VersionPredicate("FooBar (<1.2)")) - self.assertNotIn(dists[2], filtered) - self.assertNotIn(dists[3], filtered) - self.assertIn(dists[0], filtered) - self.assertIn(dists[1], filtered) + filtered = releases.filter(VersionPredicate("FooBar (<1.2)")) + self.assertNotIn(releases[2], filtered) + self.assertNotIn(releases[3], filtered) + self.assertIn(releases[0], filtered) + self.assertIn(releases[1], filtered) - def test_append(self): + def test_add_release(self): # When adding a new item to the list, the behavior is to test if - # a distribution with the same name and version number already exists, - # and if so, to add url informations to the existing PyPIDistribution + # a release with the same name and version number already exists, + # and if so, to add a new distribution for it. If the distribution type + # is already defined too, add url informations to the existing DistInfo # object. - # If no object matches, just add "normally" the object to the list. - dists = Dists([ - Dist("FooBar", "1.1", url="external_url", type="source"), + releases = ReleasesList("FooBar", [ + ReleaseInfo("FooBar", "1.1", url="external_url", + dist_type="sdist"), ]) - self.assertEqual(1, len(dists)) - dists.append(Dist("FooBar", "1.1", url="internal_url", - url_is_external=False, type="source")) - self.assertEqual(1, len(dists)) - self.assertEqual(2, len(dists[0]._urls)) + self.assertEqual(1, len(releases)) + releases.add_release(release=ReleaseInfo("FooBar", "1.1", + url="internal_url", + is_external=False, + dist_type="sdist")) + self.assertEqual(1, len(releases)) + self.assertEqual(2, len(releases[0]['sdist'].urls)) - dists.append(Dist("Foobar", "1.1.1", type="source")) - self.assertEqual(2, len(dists)) + releases.add_release(release=ReleaseInfo("FooBar", "1.1.1", + dist_type="sdist")) + self.assertEqual(2, len(releases)) # when adding a distribution whith a different type, a new distribution # has to be added. - dists.append(Dist("Foobar", "1.1.1", type="binary")) - self.assertEqual(3, len(dists)) + releases.add_release(release=ReleaseInfo("FooBar", "1.1.1", + dist_type="bdist")) + self.assertEqual(2, len(releases)) + self.assertEqual(2, len(releases[1].dists)) def test_prefer_final(self): # Can order the distributions using prefer_final - fb10 = Dist("FooBar", "1.0") # final distribution - fb11a = Dist("FooBar", "1.1a1") # alpha - fb12a = Dist("FooBar", "1.2a1") # alpha - fb12b = Dist("FooBar", "1.2b1") # beta - dists = Dists([fb10, fb11a, fb12a, fb12b]) + fb10 = ReleaseInfo("FooBar", "1.0") # final distribution + fb11a = ReleaseInfo("FooBar", "1.1a1") # alpha + fb12a = ReleaseInfo("FooBar", "1.2a1") # alpha + fb12b = ReleaseInfo("FooBar", "1.2b1") # beta + dists = ReleasesList("FooBar", [fb10, fb11a, fb12a, fb12b]) - dists.sort_distributions(prefer_final=True) + dists.sort_releases(prefer_final=True) self.assertEqual(fb10, dists[0]) - dists.sort_distributions(prefer_final=False) + dists.sort_releases(prefer_final=False) self.assertEqual(fb12b, dists[0]) - def test_prefer_source(self): - # Ordering support prefer_source - fb_source = Dist("FooBar", "1.0", type="source") - fb_binary = Dist("FooBar", "1.0", type="binary") - fb2_binary = Dist("FooBar", "2.0", type="binary") - dists = Dists([fb_binary, fb_source]) - - dists.sort_distributions(prefer_source=True) - self.assertEqual(fb_source, dists[0]) - - dists.sort_distributions(prefer_source=False) - self.assertEqual(fb_binary, dists[0]) - - dists.append(fb2_binary) - dists.sort_distributions(prefer_source=True) - self.assertEqual(fb2_binary, dists[0]) - - def test_get_same_name_and_version(self): - # PyPIDistributions can return a list of "duplicates" - fb_source = Dist("FooBar", "1.0", type="source") - fb_binary = Dist("FooBar", "1.0", type="binary") - fb2_binary = Dist("FooBar", "2.0", type="binary") - dists = Dists([fb_binary, fb_source, fb2_binary]) - duplicates = dists.get_same_name_and_version() - self.assertTrue(1, len(duplicates)) - self.assertIn(fb_source, duplicates[0]) +# def test_prefer_source(self): +# # Ordering support prefer_source +# fb_source = Dist("FooBar", "1.0", type="source") +# fb_binary = Dist("FooBar", "1.0", type="binary") +# fb2_binary = Dist("FooBar", "2.0", type="binary") +# dists = ReleasesList([fb_binary, fb_source]) +# +# dists.sort_distributions(prefer_source=True) +# self.assertEqual(fb_source, dists[0]) +# +# dists.sort_distributions(prefer_source=False) +# self.assertEqual(fb_binary, dists[0]) +# +# dists.append(fb2_binary) +# dists.sort_distributions(prefer_source=True) +# self.assertEqual(fb2_binary, dists[0]) def test_suite(): suite = unittest.TestSuite() - suite.addTest(unittest.makeSuite(TestPyPIDistribution)) - suite.addTest(unittest.makeSuite(TestPyPIDistributions)) + suite.addTest(unittest.makeSuite(TestDistInfo)) + suite.addTest(unittest.makeSuite(TestReleaseInfo)) + suite.addTest(unittest.makeSuite(TestReleasesList)) return suite if __name__ == '__main__': diff --git a/src/distutils2/tests/test_pypi_simple.py b/src/distutils2/tests/test_index_simple.py rename from src/distutils2/tests/test_pypi_simple.py rename to src/distutils2/tests/test_index_simple.py --- a/src/distutils2/tests/test_pypi_simple.py +++ b/src/distutils2/tests/test_index_simple.py @@ -3,38 +3,34 @@ """ import sys import os -import shutil -import tempfile import urllib2 -from distutils2.pypi import simple -from distutils2.tests import support, run_unittest +from distutils2.index.simple import Crawler +from distutils2.tests import support from distutils2.tests.support import unittest from distutils2.tests.pypi_server import (use_pypi_server, PyPIServer, PYPI_DEFAULT_STATIC_PATH) -class PyPISimpleTestCase(support.TempdirManager, - unittest.TestCase): +class SimpleCrawlerTestCase(support.TempdirManager, unittest.TestCase): - def _get_simple_index(self, server, base_url="/simple/", hosts=None, + def _get_simple_crawler(self, server, base_url="/simple/", hosts=None, *args, **kwargs): - """Build and return a SimpleSimpleIndex instance, with the test server + """Build and return a SimpleIndex instance, with the test server urls """ if hosts is None: hosts = (server.full_address.strip("http://"),) kwargs['hosts'] = hosts - if not 'mirrors' in kwargs: - kwargs['mirrors'] = [] # to speed up tests - return simple.SimpleIndex(server.full_address + base_url, *args, + return Crawler(server.full_address + base_url, *args, **kwargs) - def test_bad_urls(self): - index = simple.SimpleIndex(mirrors=[]) + @use_pypi_server() + def test_bad_urls(self, server): + crawler = Crawler() url = 'http://127.0.0.1:0/nonesuch/test_simple' try: - v = index._open_url(url) + v = crawler._open_url(url) except Exception, v: self.assertTrue(url in str(v)) else: @@ -43,10 +39,10 @@ # issue 16 # easy_install inquant.contentmirror.plone breaks because of a typo # in its home URL - index = simple.SimpleIndex(hosts=('www.example.com',), mirrors=[]) + crawler = Crawler(hosts=('example.org',)) url = 'url:%20https://svn.plone.org/svn/collective/inquant.contentmirror.plone/trunk' try: - v = index._open_url(url) + v = crawler._open_url(url) except Exception, v: self.assertTrue(url in str(v)) else: @@ -58,10 +54,10 @@ old_urlopen = urllib2.urlopen urllib2.urlopen = _urlopen - url = 'http://example.com' + url = 'http://example.org' try: try: - v = index._open_url(url) + v = crawler._open_url(url) except Exception, v: self.assertTrue('line' in str(v)) else: @@ -72,92 +68,91 @@ # issue 20 url = 'http://http://svn.pythonpaste.org/Paste/wphp/trunk' try: - index._open_url(url) + crawler._open_url(url) except Exception, v: self.assertTrue('nonnumeric port' in str(v)) # issue #160 if sys.version_info[0] == 2 and sys.version_info[1] == 7: # this should not fail - url = 'http://example.com' + url = server.full_address page = ('') - index._process_url(url, page) + crawler._process_url(url, page) @use_pypi_server("test_found_links") def test_found_links(self, server): - # Browse the index, asking for a specified distribution version + # Browse the index, asking for a specified release version # The PyPI index contains links for version 1.0, 1.1, 2.0 and 2.0.1 - index = self._get_simple_index(server) - last_distribution = index.get("foobar") + crawler = self._get_simple_crawler(server) + last_release = crawler.get("foobar") # we have scanned the index page self.assertIn(server.full_address + "/simple/foobar/", - index._processed_urls) + crawler._processed_urls) - # we have found 4 distributions in this page - self.assertEqual(len(index._distributions["foobar"]), 4) + # we have found 4 releases in this page + self.assertEqual(len(crawler._releases["foobar"]), 4) # and returned the most recent one - self.assertEqual("%s" % last_distribution.version, '2.0.1') + self.assertEqual("%s" % last_release.version, '2.0.1') def test_is_browsable(self): - index = simple.SimpleIndex(follow_externals=False, mirrors=[]) - self.assertTrue(index._is_browsable(index.index_url + "test")) + crawler = Crawler(follow_externals=False) + self.assertTrue(crawler._is_browsable(crawler.index_url + "test")) # Now, when following externals, we can have a list of hosts to trust. # and don't follow other external links than the one described here. - index = simple.SimpleIndex(hosts=["pypi.python.org", "test.org"], - follow_externals=True, mirrors=[]) + crawler = Crawler(hosts=["pypi.python.org", "example.org"], + follow_externals=True) good_urls = ( "http://pypi.python.org/foo/bar", "http://pypi.python.org/simple/foobar", - "http://test.org", - "http://test.org/", - "http://test.org/simple/", + "http://example.org", + "http://example.org/", + "http://example.org/simple/", ) bad_urls = ( "http://python.org", - "http://test.tld", + "http://example.tld", ) for url in good_urls: - self.assertTrue(index._is_browsable(url)) + self.assertTrue(crawler._is_browsable(url)) for url in bad_urls: - self.assertFalse(index._is_browsable(url)) + self.assertFalse(crawler._is_browsable(url)) # allow all hosts - index = simple.SimpleIndex(follow_externals=True, hosts=("*",), - mirrors=[]) - self.assertTrue(index._is_browsable("http://an-external.link/path")) - self.assertTrue(index._is_browsable("pypi.test.tld/a/path")) + crawler = Crawler(follow_externals=True, hosts=("*",)) + self.assertTrue(crawler._is_browsable("http://an-external.link/path")) + self.assertTrue(crawler._is_browsable("pypi.example.org/a/path")) # specify a list of hosts we want to allow - index = simple.SimpleIndex(follow_externals=True, - hosts=("*.test.tld",), mirrors=[]) - self.assertFalse(index._is_browsable("http://an-external.link/path")) - self.assertTrue(index._is_browsable("http://pypi.test.tld/a/path")) + crawler = Crawler(follow_externals=True, + hosts=("*.example.org",)) + self.assertFalse(crawler._is_browsable("http://an-external.link/path")) + self.assertTrue(crawler._is_browsable("http://pypi.example.org/a/path")) @use_pypi_server("with_externals") - def test_restrict_hosts(self, server): + def test_follow_externals(self, server): # Include external pages # Try to request the package index, wich contains links to "externals" # resources. They have to be scanned too. - index = self._get_simple_index(server, follow_externals=True) - index.get("foobar") + crawler = self._get_simple_crawler(server, follow_externals=True) + crawler.get("foobar") self.assertIn(server.full_address + "/external/external.html", - index._processed_urls) + crawler._processed_urls) @use_pypi_server("with_real_externals") def test_restrict_hosts(self, server): # Only use a list of allowed hosts is possible # Test that telling the simple pyPI client to not retrieve external # works - index = self._get_simple_index(server, follow_externals=False) - index.get("foobar") + crawler = self._get_simple_crawler(server, follow_externals=False) + crawler.get("foobar") self.assertNotIn(server.full_address + "/external/external.html", - index._processed_urls) + crawler._processed_urls) @use_pypi_server(static_filesystem_paths=["with_externals"], static_uri_paths=["simple", "external"]) @@ -171,23 +166,26 @@ # - someone manually coindexes this link (with the md5 in the url) onto # an external page accessible from the package page. # - someone reuploads the package (with a different md5) - # - while easy_installing, an MD5 error occurs because the external link - # is used + # - while easy_installing, an MD5 error occurs because the external + # link is used # -> The index should use the link from pypi, not the external one. # start an index server index_url = server.full_address + '/simple/' # scan a test index - index = simple.SimpleIndex(index_url, follow_externals=True, mirrors=[]) - dists = index.find("foobar") + crawler = Crawler(index_url, follow_externals=True) + releases = crawler.find("foobar") server.stop() # we have only one link, because links are compared without md5 - self.assertEqual(len(dists), 1) + self.assertEqual(1, len(releases)) + self.assertEqual(1, len(releases[0].dists)) # the link should be from the index - self.assertEqual('12345678901234567', dists[0].url['hashval']) - self.assertEqual('md5', dists[0].url['hashname']) + self.assertEqual(2, len(releases[0].dists['sdist'].urls)) + self.assertEqual('12345678901234567', + releases[0].dists['sdist'].url['hashval']) + self.assertEqual('md5', releases[0].dists['sdist'].url['hashname']) @use_pypi_server(static_filesystem_paths=["with_norel_links"], static_uri_paths=["simple", "external"]) @@ -197,22 +195,22 @@ # to not be processed by the package index, while processing "pages". # process the pages - index = self._get_simple_index(server, follow_externals=True) - index.find("foobar") + crawler = self._get_simple_crawler(server, follow_externals=True) + crawler.find("foobar") # now it should have processed only pages with links rel="download" # and rel="homepage" self.assertIn("%s/simple/foobar/" % server.full_address, - index._processed_urls) # it's the simple index page + crawler._processed_urls) # it's the simple index page self.assertIn("%s/external/homepage.html" % server.full_address, - index._processed_urls) # the external homepage is rel="homepage" + crawler._processed_urls) # the external homepage is rel="homepage" self.assertNotIn("%s/external/nonrel.html" % server.full_address, - index._processed_urls) # this link contains no rel=* + crawler._processed_urls) # this link contains no rel=* self.assertNotIn("%s/unrelated-0.2.tar.gz" % server.full_address, - index._processed_urls) # linked from simple index (no rel) + crawler._processed_urls) # linked from simple index (no rel) self.assertIn("%s/foobar-0.1.tar.gz" % server.full_address, - index._processed_urls) # linked from simple index (rel) + crawler._processed_urls) # linked from simple index (rel) self.assertIn("%s/foobar-2.0.tar.gz" % server.full_address, - index._processed_urls) # linked from external homepage (rel) + crawler._processed_urls) # linked from external homepage (rel) def test_uses_mirrors(self): # When the main repository seems down, try using the given mirrors""" @@ -222,18 +220,18 @@ try: # create the index using both servers - index = simple.SimpleIndex(server.full_address + "/simple/", + crawler = Crawler(server.full_address + "/simple/", hosts=('*',), timeout=1, # set the timeout to 1s for the tests - mirrors=[mirror.full_address + "/simple/",]) + mirrors=[mirror.full_address + "/simple/", ]) # this should not raise a timeout - self.assertEqual(4, len(index.find("foo"))) + self.assertEqual(4, len(crawler.find("foo"))) finally: mirror.stop() def test_simple_link_matcher(self): # Test that the simple link matcher yields the right links""" - index = simple.SimpleIndex(follow_externals=False, mirrors=[]) + crawler = Crawler(follow_externals=False) # Here, we define: # 1. one link that must be followed, cause it's a download one @@ -241,27 +239,27 @@ # returns false for it. # 3. one link that must be followed cause it's a homepage that is # browsable - self.assertTrue(index._is_browsable("%stest" % index.index_url)) - self.assertFalse(index._is_browsable("http://dl-link2")) + self.assertTrue(crawler._is_browsable("%stest" % crawler.index_url)) + self.assertFalse(crawler._is_browsable("http://dl-link2")) content = """ download_link1 homepage_link1 homepage_link2 - """ % index.index_url + """ % crawler.index_url # Test that the simple link matcher yield the good links. - generator = index._simple_link_matcher(content, index.index_url) + generator = crawler._simple_link_matcher(content, crawler.index_url) self.assertEqual(('http://dl-link1', True), generator.next()) - self.assertEqual(('%stest' % index.index_url, False), + self.assertEqual(('%stest' % crawler.index_url, False), generator.next()) self.assertRaises(StopIteration, generator.next) # Follow the external links is possible - index.follow_externals = True - generator = index._simple_link_matcher(content, index.index_url) + crawler.follow_externals = True + generator = crawler._simple_link_matcher(content, crawler.index_url) self.assertEqual(('http://dl-link1', True), generator.next()) self.assertEqual(('http://dl-link2', False), generator.next()) - self.assertEqual(('%stest' % index.index_url, False), + self.assertEqual(('%stest' % crawler.index_url, False), generator.next()) self.assertRaises(StopIteration, generator.next) @@ -269,19 +267,19 @@ # Test that we can browse local files""" index_path = os.sep.join(["file://" + PYPI_DEFAULT_STATIC_PATH, "test_found_links", "simple"]) - index = simple.SimpleIndex(index_path) - dists = index.find("foobar") + crawler = Crawler(index_path) + dists = crawler.find("foobar") self.assertEqual(4, len(dists)) def test_get_link_matcher(self): - crawler = simple.SimpleIndex("http://example.org") + crawler = Crawler("http://example.org") self.assertEqual('_simple_link_matcher', crawler._get_link_matcher( "http://example.org/some/file").__name__) self.assertEqual('_default_link_matcher', crawler._get_link_matcher( "http://other-url").__name__) def test_default_link_matcher(self): - crawler = simple.SimpleIndex("http://example.org", mirrors=[]) + crawler = Crawler("http://example.org", mirrors=[]) crawler.follow_externals = True crawler._is_browsable = lambda *args:True base_url = "http://example.org/some/file/" @@ -297,7 +295,7 @@ self.assertIn('http://example.org/some/download', found_links) def test_suite(): - return unittest.makeSuite(PyPISimpleTestCase) + return unittest.makeSuite(SimpleCrawlerTestCase) if __name__ == '__main__': unittest.main(defaultTest="test_suite") -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:46 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:46 +0200 Subject: [Python-checkins] distutils2: Rename metadata `from_dict` argument and method to `mapping` and `update`. Message-ID: tarek.ziade pushed e61b7c73e560 to distutils2: http://hg.python.org/distutils2/rev/e61b7c73e560 changeset: 452:e61b7c73e560 user: Alexis Metaireau date: Wed Jul 21 14:24:05 2010 +0200 summary: Rename metadata `from_dict` argument and method to `mapping` and `update`. files: src/distutils2/metadata.py diff --git a/src/distutils2/metadata.py b/src/distutils2/metadata.py --- a/src/distutils2/metadata.py +++ b/src/distutils2/metadata.py @@ -188,7 +188,7 @@ "set" method, building the metadata from the dict. """ def __init__(self, path=None, platform_dependent=False, - execution_context=None, fileobj=None, from_dict=None): + execution_context=None, fileobj=None, mapping=None): self._fields = {} self.version = None self.docutils_support = _HAS_DOCUTILS @@ -198,8 +198,8 @@ elif fileobj is not None: self.read_file(fileobj) self.execution_context = execution_context - if from_dict: - self.set_from_dict(from_dict) + if mapping: + self.update(mapping) def _set_best_version(self): self.version = _best_version(self._fields) @@ -335,21 +335,35 @@ for value in values: self._write_field(fileobject, field, value) - def set_from_dict(self, from_dict): - """Set metadata values from the given dict. + def update(self, other=None, **kwargs): + """Set metadata values from the given mapping If overwrite is set to False, just add metadata values that are actually not defined. If there is existing values in conflict with the dictionary ones, the - dictionary values prevails. - + new values prevails. + Empty values (e.g. None and []) are not setted this way. """ - for key, value in from_dict.items(): + def _set(key, value): if value not in ([], None): self.set(key, value) + if other is None: + pass + elif hasattr(other, 'iteritems'): # iteritems saves memory and lookups + for k, v in other.iteritems(): + _set(k, v) + elif hasattr(other, 'keys'): + for k in other.keys(): + _set(k, v) + else: + for k, v in other: + _set(k, v) + if kwargs: + self.update(kwargs) + def set(self, name, value): """Controls then sets a metadata field""" name = self._convert_name(name) -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:46 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:46 +0200 Subject: [Python-checkins] distutils2: Raise an exception if a project is not found, while querying for releases of Message-ID: tarek.ziade pushed 259f647d50ee to distutils2: http://hg.python.org/distutils2/rev/259f647d50ee changeset: 461:259f647d50ee user: Alexis Metaireau date: Tue Jul 27 15:23:04 2010 +0200 summary: Raise an exception if a project is not found, while querying for releases of files: src/distutils2/index/simple.py diff --git a/src/distutils2/index/simple.py b/src/distutils2/index/simple.py --- a/src/distutils2/index/simple.py +++ b/src/distutils2/index/simple.py @@ -18,7 +18,7 @@ get_infos_from_url, MD5_HASH) from distutils2.index.errors import (IndexesError, DownloadError, UnableToDownload, CantParseArchiveName, - ReleaseNotFound) + ReleaseNotFound, ProjectNotFound) from distutils2.index.mirrors import get_mirrors from distutils2 import __version__ as __distutils2_version__ @@ -157,11 +157,12 @@ predicate = self._get_version_predicate(requirements) prefer_final = self._get_prefer_final(prefer_final) self._process_index_page(predicate.name) - releases = self._projects.setdefault(predicate.name, - ReleasesList(predicate.name)) - if releases: - releases = releases.filter(predicate) - releases.sort_releases(prefer_final=prefer_final) + + if not self._projects.has_key(predicate.name): + raise ProjectNotFound() + + releases = self._projects.get(predicate) + releases.sort_releases(prefer_final=prefer_final) return releases def get_release(self, requirements, prefer_final=None): -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:46 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:46 +0200 Subject: [Python-checkins] distutils2: Fix bad behavior while browsing the simple index. Message-ID: tarek.ziade pushed 45e7efdb6985 to distutils2: http://hg.python.org/distutils2/rev/45e7efdb6985 changeset: 451:45e7efdb6985 user: Alexis Metaireau date: Wed Jul 21 11:26:42 2010 +0200 summary: Fix bad behavior while browsing the simple index. files: src/distutils2/index/dist.py, src/distutils2/index/simple.py, src/distutils2/tests/test_index_simple.py diff --git a/src/distutils2/index/dist.py b/src/distutils2/index/dist.py --- a/src/distutils2/index/dist.py +++ b/src/distutils2/index/dist.py @@ -123,6 +123,9 @@ raise TypeError("cannot compare %s and %s" % (self.name, other.name)) + def __repr__(self): + return "<%s %s>" %(self.name, self.version) + def __eq__(self, other): self._check_is_comparable(other) return self.version == other.version diff --git a/src/distutils2/index/simple.py b/src/distutils2/index/simple.py --- a/src/distutils2/index/simple.py +++ b/src/distutils2/index/simple.py @@ -14,7 +14,7 @@ from distutils2.index.base import IndexClient from distutils2.index.dist import (ReleasesList, EXTENSIONS, - get_infos_from_url) + get_infos_from_url, MD5_HASH) from distutils2.index.errors import (IndexError, DownloadError, UnableToDownload) from distutils2.index.mirrors import get_mirrors @@ -30,9 +30,6 @@ # -- Regexps ------------------------------------------------- EGG_FRAGMENT = re.compile(r'^egg=([-A-Za-z0-9_.]+)$') HREF = re.compile("""href\\s*=\\s*['"]?([^'"> ]+)""", re.I) -PYPI_MD5 = re.compile( - '([^<]+)\n\s+\\(md5\\)') URL_SCHEME = re.compile('([-+.a-z0-9]{2,}):', re.I).match # This pattern matches a character entity reference (a decimal numeric @@ -238,6 +235,9 @@ else: return self._default_link_matcher + def _get_full_url(self, url, base_url): + return urlparse.urljoin(base_url, self._htmldecode(url)) + def _simple_link_matcher(self, content, base_url): """Yield all links with a rel="download" or rel="homepage". @@ -245,23 +245,27 @@ If follow_externals is set to False, dont yeld the external urls. """ + for match in HREF.finditer(content): + url = self._get_full_url(match.group(1), base_url) + if MD5_HASH.match(url): + yield (url, True) + for match in REL.finditer(content): + # search for rel links. tag, rel = match.groups() rels = map(str.strip, rel.lower().split(',')) if 'homepage' in rels or 'download' in rels: for match in HREF.finditer(tag): - url = urlparse.urljoin(base_url, - self._htmldecode(match.group(1))) + url = self._get_full_url(match.group(1), base_url) if 'download' in rels or self._is_browsable(url): # yield a list of (url, is_download) - yield (urlparse.urljoin(base_url, url), - 'download' in rels) + yield (url, 'download' in rels) def _default_link_matcher(self, content, base_url): """Yield all links found on the page. """ for match in HREF.finditer(content): - url = urlparse.urljoin(base_url, self._htmldecode(match.group(1))) + url = self._get_full_url(match.group(1), base_url) if self._is_browsable(url): yield (url, False) diff --git a/src/distutils2/tests/test_index_simple.py b/src/distutils2/tests/test_index_simple.py --- a/src/distutils2/tests/test_index_simple.py +++ b/src/distutils2/tests/test_index_simple.py @@ -239,24 +239,30 @@ # returns false for it. # 3. one link that must be followed cause it's a homepage that is # browsable + # 4. one link that must be followed, because it contain a md5 hash self.assertTrue(crawler._is_browsable("%stest" % crawler.index_url)) self.assertFalse(crawler._is_browsable("http://dl-link2")) content = """ download_link1 homepage_link1 - homepage_link2 - """ % crawler.index_url + homepage_link2 + tarek.ziade pushed ea3793e32154 to distutils2: http://hg.python.org/distutils2/rev/ea3793e32154 changeset: 458:ea3793e32154 user: Alexis Metaireau date: Mon Jul 26 16:10:24 2010 +0200 summary: Some import stuff for indexes. files: src/distutils2/index/__init__.py, src/distutils2/index/dist.py, src/distutils2/index/simple.py, src/distutils2/index/xmlrpc.py diff --git a/src/distutils2/index/__init__.py b/src/distutils2/index/__init__.py --- a/src/distutils2/index/__init__.py +++ b/src/distutils2/index/__init__.py @@ -1,8 +1,11 @@ -"""distutils2.index +"""Package containing ways to interact with Index APIs. -Package containing ways to interact with Index APIs. -""" +""" __all__ = ['simple', + 'xmlrpc', 'dist', -] + 'errors', + 'mirrors',] + +from dist import ReleaseInfo, ReleasesList, DistInfo diff --git a/src/distutils2/index/dist.py b/src/distutils2/index/dist.py --- a/src/distutils2/index/dist.py +++ b/src/distutils2/index/dist.py @@ -26,6 +26,8 @@ from distutils2.version import suggest_normalized_version, NormalizedVersion from distutils2.metadata import DistributionMetadata +__all__ = ['ReleaseInfo', 'DistInfo', 'ReleasesList', 'get_infos_from_url'] + EXTENSIONS = ".tar.gz .tar.bz2 .tar .zip .tgz .egg".split() MD5_HASH = re.compile(r'^.*#md5=([a-f0-9]+)$') DIST_TYPES = ['bdist', 'sdist'] diff --git a/src/distutils2/index/simple.py b/src/distutils2/index/simple.py --- a/src/distutils2/index/simple.py +++ b/src/distutils2/index/simple.py @@ -22,8 +22,10 @@ from distutils2.index.mirrors import get_mirrors from distutils2 import __version__ as __distutils2_version__ +__all__ = ['Crawler', 'DEFAULT_SIMPLE_INDEX_URL'] + # -- Constants ----------------------------------------------- -DEFAULT_INDEX_URL = "http://a.pypi.python.org/simple/" +DEFAULT_SIMPLE_INDEX_URL = "http://a.pypi.python.org/simple/" DEFAULT_HOSTS = ("*",) SOCKET_TIMEOUT = 15 USER_AGENT = "Python-urllib/%s distutils2/%s" % ( @@ -105,7 +107,7 @@ on mirrors before switching. """ - def __init__(self, index_url=DEFAULT_INDEX_URL, prefer_final=False, + def __init__(self, index_url=DEFAULT_SIMPLE_INDEX_URL, prefer_final=False, prefer_source=True, hosts=DEFAULT_HOSTS, follow_externals=False, mirrors_url=None, mirrors=None, timeout=SOCKET_TIMEOUT, mirrors_max_tries=0): diff --git a/src/distutils2/index/xmlrpc.py b/src/distutils2/index/xmlrpc.py --- a/src/distutils2/index/xmlrpc.py +++ b/src/distutils2/index/xmlrpc.py @@ -6,8 +6,9 @@ from distutils2.index.errors import ProjectNotFound, InvalidSearchField from distutils2.index.dist import ReleaseInfo +__all__ = ['Client', 'DEFAULT_XMLRPC_INDEX_URL'] -PYPI_XML_RPC_URL = 'http://python.org/pypi' +DEFAULT_XMLRPC_INDEX_URL = 'http://python.org/pypi' _SEARCH_FIELDS = ['name', 'version', 'author', 'author_email', 'maintainer', 'maintainer_email', 'home_page', 'license', 'summary', @@ -18,10 +19,10 @@ """Client to query indexes using XML-RPC method calls. If no server_url is specified, use the default PyPI XML-RPC URL, - defined in the PYPI_XML_RPC_URL constant:: + defined in the DEFAULT_XMLRPC_INDEX_URL constant:: >>> client = XMLRPCClient() - >>> client.server_url == PYPI_XML_RPC_URL + >>> client.server_url == DEFAULT_XMLRPC_INDEX_URL True >>> client = XMLRPCClient("http://someurl/") @@ -29,7 +30,7 @@ 'http://someurl/' """ - def __init__(self, server_url=PYPI_XML_RPC_URL, prefer_final=False, + def __init__(self, server_url=DEFAULT_XMLRPC_INDEX_URL, prefer_final=False, prefer_source=True): super(Client, self).__init__(prefer_final, prefer_source) self.server_url = server_url -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:46 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:46 +0200 Subject: [Python-checkins] distutils2: XML-RPC client for indexes. Message-ID: tarek.ziade pushed 2413e88ec79e to distutils2: http://hg.python.org/distutils2/rev/2413e88ec79e changeset: 455:2413e88ec79e user: Alexis Metaireau date: Tue Jul 20 18:18:40 2010 +0200 summary: XML-RPC client for indexes. files: docs/source/projects-index.client.rst, docs/source/projects-index.dist.rst, docs/source/projects-index.xmlrpc.rst, src/distutils2/index/dist.py, src/distutils2/index/errors.py, src/distutils2/index/simple.py, src/distutils2/index/xmlrpc.py, src/distutils2/tests/pypi_server.py, src/distutils2/tests/test_index_xmlrpc.py diff --git a/docs/source/projects-index.client.rst b/docs/source/projects-index.client.rst --- a/docs/source/projects-index.client.rst +++ b/docs/source/projects-index.client.rst @@ -8,6 +8,9 @@ The aim of this module is to choose the best way to query the API, using the less possible XML-RPC, and when possible the simple index. +.. note:: This index is not yet available, so please rely on XMLRPC client or + Simple Crawler to browse indexes. + API === diff --git a/docs/source/projects-index.dist.rst b/docs/source/projects-index.dist.rst --- a/docs/source/projects-index.dist.rst +++ b/docs/source/projects-index.dist.rst @@ -84,4 +84,30 @@ >>> r['sdist'].url {'url': 'http://example.org/foobar-1.0.tar.gz', 'hashname': None, 'hashval': None, 'is_external': True} + +Attributes Lazy loading +----------------------- +.. note:: This is not currently available. So you have to rely on the indexes by + yourself to fill in the fields ! + +To abstract a maximum the way of querying informations to the indexes, +attributes and releases informations can be retrieved "on demand", in a "lazy" +way. + +For instance, if you have a release instance that does not contain the metadata +attribute, it can be build directly when accedded:: + + >>> r = Release("FooBar", "1.1") + >>> r.has_metadata() + False # metadata field is actually set to "None" + >>> r.metadata + + +Like this, it's possible to retrieve project's releases, releases metadata and +releases distributions informations. + +Internally, this is possible because while retrieving for the first time +informations about projects, releases or distributions, a reference to the +client used is stored in the objects. Then, while trying to access undefined +fields, it will be used if necessary. diff --git a/docs/source/projects-index.xmlrpc.rst b/docs/source/projects-index.xmlrpc.rst --- a/docs/source/projects-index.xmlrpc.rst +++ b/docs/source/projects-index.xmlrpc.rst @@ -2,21 +2,148 @@ Query indexes via XML-RPC ========================= -Indexes can be queried by using XML-RPC calls, and Distutils2 provides a simple -way to use this methods. +Indexes can be queried using XML-RPC calls, and Distutils2 provides a simple +way to interface with XML-RPC. -The :class:`distutils2.xmlrpc.Client` have some specificities, that would be -described here. +You should **use** XML-RPC when: -You should use XML-RPC for: + * Searching the index for projects **on other fields than project + names**. For instance, you can search for projects based on the + author_email field. + * Searching all the versions that have existed for a project. + * you want to retrive METADATAs informations from releases or + distributions. - * XXX TODO +You should **avoid using** XML-RPC method calls when: + + * Retrieving the last version of a project + * Getting the projects with a specific name and version. + * The simple index can match your needs + +When dealing with indexes, keep in mind that the index queriers will always +return you :class:`distutils2.index.ReleaseInfo` and +:class:`distutils2.index.ReleasesList` objects. + +Some methods here share common APIs with the one you can find on +:class:`distutils2.index.simple`, internally, :class:`distutils2.index.client` +is inherited by :class:`distutils2.index.xmlrpc.Client` API ==== -:: - >>> from distutils2.index import XmlRpcClient() - >>> client = XmlRpcClient() + +.. autoclass:: distutils2.index.xmlrpc.Client + :members: Usage examples =============== + +Use case described here are use case that are not common to the other clients. +If you want to see all the methods, please refer to API or to usage examples +described in :class:`distutils2.index.client.Client` + +Finding releases +---------------- + +It's a common use case to search for "things" within the index. +We can basically search for projects by their name, which is the +most used way for users (eg. "give me the last version of the FooBar project"). +This can be accomplished using the following syntax:: + + >>> client = XMLRPCClient() + >>> client.get("Foobar (<= 1.3)) + + >>> client.find("FooBar (<= 1.3)") + [FooBar 1.1, FooBar 1.1.1, FooBar 1.2, FooBar 1.2.1] + +And we also can find for specific fields:: + + >>> client.find_by(field=value) + +You could specify the operator to use, default is "or":: + + >>> client.find_by(field=value, operator="and") + +The specific fields you can search are: + + * name + * version + * author + * author_email + * maintainer + * maintainer_email + * home_page + * license + * summary + * description + * keywords + * platform + * download_url + +Getting metadata informations +----------------------------- + +XML-RPC is a prefered way to retrieve metadata informations from indexes. +It's really simple to do so:: + + >>> client = XMLRPCClient() + >>> client.get_metadata("FooBar", "1.1") + + +Assuming we already have a :class:`distutils2.index.ReleaseInfo` object defined, +it's possible to pass it ot the xmlrpc client to retrieve and complete it's +metadata:: + + >>> foobar11 = ReleaseInfo("FooBar", "1.1") + >>> client = XMLRPCClient() + >>> returned_release = client.get_metadata(release=foobar11) + >>> returned_release + + +Get all the releases of a project +--------------------------------- + +To retrieve all the releases for a project, you can build them using +`get_releases`:: + + >>> client = XMLRPCClient() + >>> client.get_releases("FooBar") + [, , ] + +Get informations about distributions +------------------------------------ + +Indexes have informations about projects, releases **and** distributions. +If you're not familiar with those, please refer to the documentation of +:mod:`distutils2.index.dist`. + +It's possible to retrive informations about distributions, e.g "what are the +existing distributions for this release ? How to retrieve them ?":: + + >>> client = XMLRPCClient() + >>> release = client.get_distributions("FooBar", "1.1") + >>> release.dists + {'sdist': , 'bdist': } + +As you see, this does not return a list of distributions, but a release, +because a release can be used like a list of distributions. + +Lazy load information from project, releases and distributions. +---------------------------------------------------------------- + +.. note:: The lazy loading feature is not currently available ! + +As :mod:`distutils2.index.dist` classes support "lazy" loading of +informations, you can use it while retrieving informations from XML-RPC. + +For instance, it's possible to get all the releases for a project, and to access +directly the metadata of each release, without making +:class:`distutils2.index.xmlrpc.Client` directly (they will be made, but they're +invisible to the you):: + + >>> client = XMLRPCClient() + >>> releases = client.get_releases("FooBar") + >>> releases.get_release("1.1").metadata + + +Refer to the :mod:`distutils2.index.dist` documentation for more information +about attributes lazy loading. diff --git a/src/distutils2/index/dist.py b/src/distutils2/index/dist.py --- a/src/distutils2/index/dist.py +++ b/src/distutils2/index/dist.py @@ -20,6 +20,7 @@ except ImportError: from distutils2._backport import hashlib +from distutils2.errors import IrrationalVersionError from distutils2.index.errors import (HashDoesNotMatch, UnsupportedHashName, CantParseArchiveName) from distutils2.version import suggest_normalized_version, NormalizedVersion @@ -47,14 +48,30 @@ :param kwargs: optional arguments for a new distribution. """ self.name = name - self.version = NormalizedVersion(version) - self.metadata = DistributionMetadata() # XXX from_dict=metadata) + self._version = None + self.version = version + self.metadata = DistributionMetadata(mapping=metadata) self.dists = {} self.hidden = hidden - + if 'dist_type' in kwargs: dist_type = kwargs.pop('dist_type') self.add_distribution(dist_type, **kwargs) + + def set_version(self, version): + try: + self._version = NormalizedVersion(version) + except IrrationalVersionError: + suggestion = suggest_normalized_version(version) + if suggestion: + self.version = suggestion + else: + raise IrrationalVersionError(version) + + def get_version(self): + return self._version + + version = property(get_version, set_version) @property def is_final(self): @@ -111,6 +128,9 @@ return self.get_distribution(prefer_source=prefer_source)\ .download(path=temp_path) + def set_metadata(self, metadata): + self.metadata.update(metadata) + def __getitem__(self, item): """distributions are available using release["sdist"]""" return self.dists[item] @@ -262,13 +282,12 @@ Provides useful methods and facilities to sort and filter releases. """ - def __init__(self, name, list=[], contains_hidden=False): + def __init__(self, name, releases=[], contains_hidden=False): super(ReleasesList, self).__init__() - for item in list: - self.append(item) self.name = name self.contains_hidden = contains_hidden - + self.add_releases(releases) + def filter(self, predicate): """Filter and return a subset of releases matching the given predicate. """ @@ -286,6 +305,14 @@ releases.sort_releases(prefer_final, reverse=True) return releases[0] + def add_releases(self, releases): + """Add releases in the release list. + + :param: releases is a list of ReleaseInfo objects. + """ + for r in releases: + self.add_release(release=r) + def add_release(self, version=None, dist_type='sdist', release=None, **dist_args): """Add a release to the list. @@ -302,6 +329,9 @@ if release.name != self.name: raise ValueError(release.name) version = '%s' % release.version + if not version in self.get_versions(): + # append only if not already exists + self.append(release) for dist in release.dists.values(): for url in dist.urls: self.add_release(version, dist.dist_type, **url) @@ -350,6 +380,12 @@ """Return a list of releases versions contained""" return ["%s" % r.version for r in self] + def __repr__(self): + string = 'Project "%s"' % self.name + if self.get_versions(): + string += ' versions: %s' % ', '.join(self.get_versions()) + return '<%s>' % string + def get_infos_from_url(url, probable_dist_name=None, is_external=True): """Get useful informations from an URL. diff --git a/src/distutils2/index/errors.py b/src/distutils2/index/errors.py --- a/src/distutils2/index/errors.py +++ b/src/distutils2/index/errors.py @@ -9,6 +9,10 @@ """The base class for errors of the index python package.""" +class ProjectNotFound(IndexError): + """Project has not been found""" + + class DistributionNotFound(IndexError): """No distribution match the given requirements.""" @@ -31,3 +35,7 @@ class UnableToDownload(IndexError): """All mirrors have been tried, without success""" + + +class InvalidSearchField(IndexError): + """An invalid search field has been used""" diff --git a/src/distutils2/index/simple.py b/src/distutils2/index/simple.py --- a/src/distutils2/index/simple.py +++ b/src/distutils2/index/simple.py @@ -223,7 +223,7 @@ try: infos = get_infos_from_url(link, project_name, is_external=not self.index_url in url) - except CantParseArchiveName as e: + except CantParseArchiveName, e: logging.warning("version has not been parsed: %s" % e) else: diff --git a/src/distutils2/index/xmlrpc.py b/src/distutils2/index/xmlrpc.py new file mode 100644 --- /dev/null +++ b/src/distutils2/index/xmlrpc.py @@ -0,0 +1,165 @@ +import logging +import xmlrpclib + +from distutils2.errors import IrrationalVersionError +from distutils2.index.base import IndexClient +from distutils2.index.errors import ProjectNotFound, InvalidSearchField +from distutils2.index.dist import ReleaseInfo, ReleasesList + + +PYPI_XML_RPC_URL = 'http://python.org/pypi' + +_SEARCH_FIELDS = ['name', 'version', 'author', 'author_email', 'maintainer', + 'maintainer_email', 'home_page', 'license', 'summary', + 'description', 'keywords', 'platform', 'download_url'] + + +class Client(IndexClient): + """Client to query indexes using XML-RPC method calls. + + If no server_url is specified, use the default PyPI XML-RPC URL, + defined in the PYPI_XML_RPC_URL constant:: + + >>> client = XMLRPCClient() + >>> client.server_url == PYPI_XML_RPC_URL + True + + >>> client = XMLRPCClient("http://someurl/") + >>> client.server_url + 'http://someurl/' + """ + + def __init__(self, server_url=PYPI_XML_RPC_URL, prefer_final=False): + self.server_url = server_url + self._projects = {} + self._prefer_final = prefer_final + + def _search_for_releases(self, requirements): + return self.get_releases(requirements.name) + + def _get_release(self, requirements, prefer_final=False): + releases = self.get_releases(requirements.name) + release = releases.get_last(requirements, prefer_final) + self.get_metadata(release.name, "%s" % release.version) + self.get_distributions(release.name, "%s" % release.version) + return release + + @property + def proxy(self): + """Property used to return the XMLRPC server proxy. + + If no server proxy is defined yet, creates a new one:: + + >>> client = XmlRpcClient() + >>> client.proxy() + + + """ + if not hasattr(self, '_server_proxy'): + self._server_proxy = xmlrpclib.ServerProxy(self.server_url) + + return self._server_proxy + + def _get_project(self, project_name): + """Return an project instance, create it if necessary""" + return self._projects.setdefault(project_name, + ReleasesList(project_name)) + + def get_releases(self, project_name, show_hidden=True, force_update=False): + """Return the list of existing releases for a specific project. + + Cache the results from one call to another. + + If show_hidden is True, return the hidden releases too. + If force_update is True, reprocess the index to update the + informations (eg. make a new XML-RPC call). + :: + + >>> client = XMLRPCClient() + >>> client.get_releases('Foo') + ['1.1', '1.2', '1.3'] + + If no such project exists, raise a ProjectNotFound exception:: + + >>> client.get_project_versions('UnexistingProject') + ProjectNotFound: UnexistingProject + + """ + def get_versions(project_name, show_hidden): + return self.proxy.package_releases(project_name, show_hidden) + + if not force_update and (project_name in self._projects): + project = self._projects[project_name] + if not project.contains_hidden and show_hidden: + # if hidden releases are requested, and have an existing + # list of releases that does not contains hidden ones + all_versions = get_versions(project_name, show_hidden) + existing_versions = project.get_versions() + hidden_versions = list(set(all_versions) - + set(existing_versions)) + for version in hidden_versions: + project.add_release(release=ReleaseInfo(project_name, + version)) + return project + else: + versions = get_versions(project_name, show_hidden) + if not versions: + raise ProjectNotFound(project_name) + project = self._get_project(project_name) + project.add_releases([ReleaseInfo(project_name, version) + for version in versions]) + return project + + def get_distributions(self, project_name, version): + """Grab informations about distributions from XML-RPC. + + Return a ReleaseInfo object, with distribution-related informations + filled in. + """ + url_infos = self.proxy.release_urls(project_name, version) + project = self._get_project(project_name) + if version not in project.get_versions(): + project.add_release(release=ReleaseInfo(project_name, version)) + release = project.get_release(version) + for info in url_infos: + packagetype = info['packagetype'] + dist_infos = {'url': info['url'], + 'hashval': info['md5_digest'], + 'hashname': 'md5', + 'is_external': False} + release.add_distribution(packagetype, **dist_infos) + return release + + def get_metadata(self, project_name, version): + """Retreive project metadatas. + + Return a ReleaseInfo object, with metadata informations filled in. + """ + metadata = self.proxy.release_data(project_name, version) + project = self._get_project(project_name) + if version not in project.get_versions(): + project.add_release(release=ReleaseInfo(project_name, version)) + release = project.get_release(version) + release.set_metadata(metadata) + return release + + def search(self, name=None, operator="or", **kwargs): + """Find using the keys provided in kwargs. + + You can set operator to "and" or "or". + """ + for key in kwargs: + if key not in _SEARCH_FIELDS: + raise InvalidSearchField(key) + if name: + kwargs["name"] = name + projects = self.proxy.search(kwargs, operator) + for p in projects: + project = self._get_project(p['name']) + try: + project.add_release(release=ReleaseInfo(p['name'], + p['version'], metadata={'summary':p['summary']})) + except IrrationalVersionError, e: + logging.warn("Irrational version error found: %s" % e) + + return [self._projects[p['name']] for p in projects] diff --git a/src/distutils2/tests/pypi_server.py b/src/distutils2/tests/pypi_server.py --- a/src/distutils2/tests/pypi_server.py +++ b/src/distutils2/tests/pypi_server.py @@ -1,4 +1,4 @@ -"""Mocked PyPI Server implementation, to use in tests. +"""Mock PyPI Server implementation, to use in tests. This module also provides a simple test case to extend if you need to use the PyPIServer all along your test case. Be sure to read the documentation @@ -6,18 +6,28 @@ """ import Queue +import SocketServer +import os.path +import select +import socket import threading -import time -import urllib2 + from BaseHTTPServer import HTTPServer from SimpleHTTPServer import SimpleHTTPRequestHandler -import os.path -import select +from SimpleXMLRPCServer import SimpleXMLRPCServer from distutils2.tests.support import unittest PYPI_DEFAULT_STATIC_PATH = os.path.dirname(os.path.abspath(__file__)) + "/pypiserver" +def use_xmlrpc_server(*server_args, **server_kwargs): + server_kwargs['serve_xmlrpc'] = True + return use_pypi_server(*server_args, **server_kwargs) + +def use_http_server(*server_args, **server_kwargs): + server_kwargs['serve_xmlrpc'] = False + return use_pypi_server(*server_args, **server_kwargs) + def use_pypi_server(*server_args, **server_kwargs): """Decorator to make use of the PyPIServer for test methods, just when needed, and not for the entire duration of the testcase. @@ -45,45 +55,65 @@ self.pypi.stop() class PyPIServer(threading.Thread): - """PyPI Mocked server. + """PyPI Mock server. Provides a mocked version of the PyPI API's, to ease tests. Support serving static content and serving previously given text. """ def __init__(self, test_static_path=None, - static_filesystem_paths=["default"], static_uri_paths=["simple"]): + static_filesystem_paths=["default"], + static_uri_paths=["simple"], serve_xmlrpc=False) : """Initialize the server. + + Default behavior is to start the HTTP server. You can either start the + xmlrpc server by setting xmlrpc to True. Caution: Only one server will + be started. static_uri_paths and static_base_path are parameters used to provides respectively the http_paths to serve statically, and where to find the matching files on the filesystem. """ + # we want to launch the server in a new dedicated thread, to not freeze + # tests. threading.Thread.__init__(self) self._run = True - self.httpd = HTTPServer(('', 0), PyPIRequestHandler) - self.httpd.RequestHandlerClass.log_request = lambda *_: None - self.httpd.RequestHandlerClass.pypi_server = self - self.address = (self.httpd.server_name, self.httpd.server_port) - self.request_queue = Queue.Queue() - self._requests = [] - self.default_response_status = 200 - self.default_response_headers = [('Content-type', 'text/plain')] - self.default_response_data = "hello" - - # initialize static paths / filesystems - self.static_uri_paths = static_uri_paths - if test_static_path is not None: - static_filesystem_paths.append(test_static_path) - self.static_filesystem_paths = [PYPI_DEFAULT_STATIC_PATH + "/" + path - for path in static_filesystem_paths] + self._serve_xmlrpc = serve_xmlrpc + + if not self._serve_xmlrpc: + self.server = HTTPServer(('', 0), PyPIRequestHandler) + self.server.RequestHandlerClass.pypi_server = self + + self.request_queue = Queue.Queue() + self._requests = [] + self.default_response_status = 200 + self.default_response_headers = [('Content-type', 'text/plain')] + self.default_response_data = "hello" + + # initialize static paths / filesystems + self.static_uri_paths = static_uri_paths + if test_static_path is not None: + static_filesystem_paths.append(test_static_path) + self.static_filesystem_paths = [PYPI_DEFAULT_STATIC_PATH + "/" + path + for path in static_filesystem_paths] + else: + # xmlrpc server + self.server = PyPIXMLRPCServer(('', 0)) + self.xmlrpc = XMLRPCMockIndex() + # register the xmlrpc methods + self.server.register_introspection_functions() + self.server.register_instance(self.xmlrpc) + + self.address = (self.server.server_name, self.server.server_port) + # to not have unwanted outputs. + self.server.RequestHandlerClass.log_request = lambda *_: None def run(self): # loop because we can't stop it otherwise, for python < 2.6 while self._run: - r, w, e = select.select([self.httpd], [], [], 0.5) + r, w, e = select.select([self.server], [], [], 0.5) if r: - self.httpd.handle_request() + self.server.handle_request() def stop(self): """self shutdown is not supported for python < 2.6""" @@ -193,3 +223,180 @@ self.send_header(header, value) self.end_headers() self.wfile.write(data) + +class PyPIXMLRPCServer(SimpleXMLRPCServer): + def server_bind(self): + """Override server_bind to store the server name.""" + SocketServer.TCPServer.server_bind(self) + host, port = self.socket.getsockname()[:2] + self.server_name = socket.getfqdn(host) + self.server_port = port + +class MockDist(object): + """Fake distribution, used in the Mock PyPI Server""" + def __init__(self, name, version="1.0", hidden=False, url="http://url/", + type="source", filename="", size=10000, + digest="123456", downloads=7, has_sig=False, + python_version="source", comment="comment", + author="John Doe", author_email="john at doe.name", + maintainer="Main Tayner", maintainer_email="maintainer_mail", + project_url="http://project_url/", homepage="http://homepage/", + keywords="", platform="UNKNOWN", classifiers=[], licence="", + description="Description", summary="Summary", stable_version="", + ordering="", documentation_id="", code_kwalitee_id="", + installability_id="", obsoletes=[], obsoletes_dist=[], + provides=[], provides_dist=[], requires=[], requires_dist=[], + requires_external=[], requires_python=""): + + # basic fields + self.name = name + self.version = version + self.hidden = hidden + + # URL infos + self.url = url + self.digest = digest + self.downloads = downloads + self.has_sig = has_sig + self.python_version = python_version + self.comment = comment + self.type = type + + # metadata + self.author = author + self.author_email = author_email + self.maintainer = maintainer + self.maintainer_email = maintainer_email + self.project_url = project_url + self.homepage = homepage + self.keywords = keywords + self.platform = platform + self.classifiers = classifiers + self.licence = licence + self.description = description + self.summary = summary + self.stable_version = stable_version + self.ordering = ordering + self.cheesecake_documentation_id = documentation_id + self.cheesecake_code_kwalitee_id = code_kwalitee_id + self.cheesecake_installability_id = installability_id + + self.obsoletes = obsoletes + self.obsoletes_dist = obsoletes_dist + self.provides = provides + self.provides_dist = provides_dist + self.requires = requires + self.requires_dist = requires_dist + self.requires_external = requires_external + self.requires_python = requires_python + + def url_infos(self): + return { + 'url': self.url, + 'packagetype': self.type, + 'filename': 'filename.tar.gz', + 'size': '6000', + 'md5_digest': self.digest, + 'downloads': self.downloads, + 'has_sig': self.has_sig, + 'python_version': self.python_version, + 'comment_text': self.comment, + } + + def metadata(self): + return { + 'maintainer': self.maintainer, + 'project_url': [self.project_url], + 'maintainer_email': self.maintainer_email, + 'cheesecake_code_kwalitee_id': self.cheesecake_code_kwalitee_id, + 'keywords': self.keywords, + 'obsoletes_dist': self.obsoletes_dist, + 'requires_external': self.requires_external, + 'author': self.author, + 'author_email': self.author_email, + 'download_url': self.url, + 'platform': self.platform, + 'version': self.version, + 'obsoletes': self.obsoletes, + 'provides': self.provides, + 'cheesecake_documentation_id': self.cheesecake_documentation_id, + '_pypi_hidden': self.hidden, + 'description': self.description, + '_pypi_ordering': 19, + 'requires_dist': self.requires_dist, + 'requires_python': self.requires_python, + 'classifiers': [], + 'name': self.name, + 'licence': self.licence, + 'summary': self.summary, + 'home_page': self.homepage, + 'stable_version': self.stable_version, + 'provides_dist': self.provides_dist, + 'requires': self.requires, + 'cheesecake_installability_id': self.cheesecake_installability_id, + } + + def search_result(self): + return { + '_pypi_ordering': 0, + 'version': self.version, + 'name': self.name, + 'summary': self.summary, + } + +class XMLRPCMockIndex(object): + """Mock XMLRPC server""" + + def __init__(self, dists=[]): + self._dists = dists + + def add_distributions(self, dists): + for dist in dists: + self._dists.append(MockDist(**dist)) + + def set_distributions(self, dists): + self._dists = [] + self.add_distributions(dists) + + def set_search_result(self, result): + """set a predefined search result""" + self._search_result = result + + def _get_search_results(self): + results = [] + for name in self._search_result: + found_dist = [d for d in self._dists if d.name == name] + if found_dist: + results.append(found_dist[0]) + else: + dist = MockDist(name) + results.append(dist) + self._dists.append(dist) + return [r.search_result() for r in results] + + def list_package(self): + return [d.name for d in self._dists] + + def package_releases(self, package_name, show_hidden=False): + if show_hidden: + # return all + return [d.version for d in self._dists if d.name == package_name] + else: + # return only un-hidden + return [d.version for d in self._dists if d.name == package_name + and not d.hidden] + + def release_urls(self, package_name, version): + return [d.url_infos() for d in self._dists + if d.name == package_name and d.version == version] + + def release_data(self, package_name, version): + release = [d for d in self._dists + if d.name == package_name and d.version == version] + if release: + return release[0].metadata() + else: + return {} + + def search(self, spec, operator="and"): + return self._get_search_results() diff --git a/src/distutils2/tests/test_index_xmlrpc.py b/src/distutils2/tests/test_index_xmlrpc.py new file mode 100644 --- /dev/null +++ b/src/distutils2/tests/test_index_xmlrpc.py @@ -0,0 +1,114 @@ +"""Tests for the distutils2.index.xmlrpc module.""" + +from distutils2.tests.pypi_server import use_xmlrpc_server +from distutils2.tests import run_unittest +from distutils2.tests.support import unittest +from distutils2.index.xmlrpc import Client, InvalidSearchField, ProjectNotFound + + +class TestXMLRPCClient(unittest.TestCase): + def _get_client(self, server, *args, **kwargs): + return Client(server.full_address, *args, **kwargs) + + @use_xmlrpc_server() + def test_search(self, server): + # test that the find method return a list of ReleasesList + client = self._get_client(server) + server.xmlrpc.set_search_result(['FooBar', 'Foo', 'FooFoo']) + results = [r.name for r in client.search(name='Foo')] + self.assertEqual(3, len(results)) + self.assertIn('FooBar', results) + self.assertIn('Foo', results) + self.assertIn('FooFoo', results) + + def test_search_bad_fields(self): + client = Client() + self.assertRaises(InvalidSearchField, client.search, invalid="test") + + @use_xmlrpc_server() + def test_find(self, server): + client = self._get_client(server) + server.xmlrpc.set_distributions([ + {'name': 'FooBar', 'version': '1.1'}, + {'name': 'FooBar', 'version': '1.2', 'url': 'http://some/url/'}, + {'name': 'FooBar', 'version': '1.3', 'url': 'http://other/url/'}, + ]) + + # use a lambda here to avoid an useless mock call + server.xmlrpc.list_releases = lambda *a, **k: ['1.1', '1.2', '1.3'] + + releases = client.find('FooBar (<=1.2)') + # dont call release_data and release_url; just return name and version. + self.assertEqual(2, len(releases)) + versions = releases.get_versions() + self.assertIn('1.1', versions) + self.assertIn('1.2', versions) + self.assertNotIn('1.3', versions) + + self.assertRaises(ProjectNotFound, client.find,'Foo') + + @use_xmlrpc_server() + def test_get_releases(self, server): + client = self._get_client(server) + server.xmlrpc.set_distributions([ + {'name':'FooBar', 'version': '0.8', 'hidden': True}, + {'name':'FooBar', 'version': '0.9', 'hidden': True}, + {'name':'FooBar', 'version': '1.1'}, + {'name':'FooBar', 'version': '1.2'}, + ]) + releases = client.get_releases('FooBar', False) + versions = releases.get_versions() + self.assertEqual(2, len(versions)) + self.assertIn('1.1', versions) + self.assertIn('1.2', versions) + + releases2 = client.get_releases('FooBar', True) + versions = releases2.get_versions() + self.assertEqual(4, len(versions)) + self.assertIn('0.8', versions) + self.assertIn('0.9', versions) + self.assertIn('1.1', versions) + self.assertIn('1.2', versions) + + @use_xmlrpc_server() + def test_get_distributions(self, server): + client = self._get_client(server) + server.xmlrpc.set_distributions([ + {'name':'FooBar', 'version': '1.1', 'url': + 'http://example.org/foobar-1.1-sdist.tar.gz', + 'digest': '1234567', 'type': 'sdist'}, + {'name':'FooBar', 'version': '1.1', 'url': + 'http://example.org/foobar-1.1-bdist.tar.gz', + 'digest': '8912345', 'type': 'bdist'}, + ]) + + releases = client.get_releases('FooBar', '1.1') + client.get_distributions('FooBar', '1.1') + release = releases.get_release('1.1') + self.assertTrue('http://example.org/foobar-1.1-sdist.tar.gz', + release['sdist'].url['url']) + self.assertTrue('http://example.org/foobar-1.1-bdist.tar.gz', + release['bdist'].url['url']) + + @use_xmlrpc_server() + def test_get_metadata(self, server): + client = self._get_client(server) + server.xmlrpc.set_distributions([ + {'name':'FooBar', + 'version': '1.1', + 'keywords': '', + 'obsoletes_dist': ['FooFoo'], + 'requires_external': ['Foo'], + }]) + release = client.get_metadata('FooBar', '1.1') + self.assertEqual(['Foo'], release.metadata['requires_external']) + self.assertEqual(['FooFoo'], release.metadata['obsoletes_dist']) + + +def test_suite(): + suite = unittest.TestSuite() + suite.addTest(unittest.makeSuite(TestXMLRPCClient)) + return suite + +if __name__ == '__main__': + run_unittest(test_suite()) -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:46 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:46 +0200 Subject: [Python-checkins] distutils2: Fix some try/except clauses in index.simple (now catchs socket timeouts too) Message-ID: tarek.ziade pushed b7300dd2930a to distutils2: http://hg.python.org/distutils2/rev/b7300dd2930a changeset: 450:b7300dd2930a user: Alexis Metaireau date: Wed Jul 21 09:58:08 2010 +0200 summary: Fix some try/except clauses in index.simple (now catchs socket timeouts too) files: src/distutils2/index/simple.py diff --git a/src/distutils2/index/simple.py b/src/distutils2/index/simple.py --- a/src/distutils2/index/simple.py +++ b/src/distutils2/index/simple.py @@ -292,41 +292,32 @@ files support. """ + scheme, netloc, path, params, query, frag = urlparse.urlparse(url) + + # authentication stuff + if scheme in ('http', 'https'): + auth, host = urllib2.splituser(netloc) + else: + auth = None + + # add index.html automatically for filesystem paths + if scheme == 'file': + if url.endswith('/'): + url += "index.html" + + # add authorization headers if auth is provided + if auth: + auth = "Basic " + \ + urllib2.unquote(auth).encode('base64').strip() + new_url = urlparse.urlunparse(( + scheme, host, path, params, query, frag)) + request = urllib2.Request(new_url) + request.add_header("Authorization", auth) + else: + request = urllib2.Request(url) + request.add_header('User-Agent', USER_AGENT) try: - scheme, netloc, path, params, query, frag = urlparse.urlparse(url) - - if scheme in ('http', 'https'): - auth, host = urllib2.splituser(netloc) - else: - auth = None - - # add index.html automatically for filesystem paths - if scheme == 'file': - if url.endswith('/'): - url += "index.html" - - if auth: - auth = "Basic " + \ - urllib2.unquote(auth).encode('base64').strip() - new_url = urlparse.urlunparse(( - scheme, host, path, params, query, frag)) - request = urllib2.Request(new_url) - request.add_header("Authorization", auth) - else: - request = urllib2.Request(url) - request.add_header('User-Agent', USER_AGENT) fp = urllib2.urlopen(request) - - if auth: - # Put authentication info back into request URL if same host, - # so that links found on the page will work - s2, h2, path2, param2, query2, frag2 = \ - urlparse.urlparse(fp.url) - if s2 == scheme and h2 == host: - fp.url = urlparse.urlunparse( - (s2, netloc, path2, param2, query2, frag2)) - - return fp except (ValueError, httplib.InvalidURL), v: msg = ' '.join([str(arg) for arg in v.args]) raise IndexError('%s %s' % (url, msg)) @@ -339,6 +330,19 @@ 'The server might be down, %s' % (url, v.line)) except httplib.HTTPException, v: raise DownloadError("Download error for %s: %s" % (url, v)) + except socket.timeout: + raise DownloadError("The server timeouted") + + if auth: + # Put authentication info back into request URL if same host, + # so that links found on the page will work + s2, h2, path2, param2, query2, frag2 = \ + urlparse.urlparse(fp.url) + if s2 == scheme and h2 == host: + fp.url = urlparse.urlunparse( + (s2, netloc, path2, param2, query2, frag2)) + + return fp def _decode_entity(self, match): what = match.group(1) -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:46 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:46 +0200 Subject: [Python-checkins] distutils2: Uniformize client APIs for requesting informations to the indexes. Message-ID: tarek.ziade pushed 386c37a009e1 to distutils2: http://hg.python.org/distutils2/rev/386c37a009e1 changeset: 457:386c37a009e1 user: Alexis Metaireau date: Sat Jul 24 02:15:46 2010 +0200 summary: Uniformize client APIs for requesting informations to the indexes. files: docs/source/projects-index.dist.rst, docs/source/projects-index.simple.rst, docs/source/projects-index.xmlrpc.rst, src/distutils2/index/base.py, src/distutils2/index/dist.py, src/distutils2/index/errors.py, src/distutils2/index/simple.py, src/distutils2/index/xmlrpc.py, src/distutils2/tests/test_index_simple.py, src/distutils2/tests/test_index_xmlrpc.py diff --git a/docs/source/projects-index.dist.rst b/docs/source/projects-index.dist.rst --- a/docs/source/projects-index.dist.rst +++ b/docs/source/projects-index.dist.rst @@ -5,14 +5,13 @@ Informations coming from indexes are represented by the classes present in the `dist` module. -.. note:: Keep in mind that each project (eg. FooBar) can have several - releases (eg. 1.1, 1.2, 1.3), and each of these releases can be - provided in multiple distributions (eg. a source distribution, - a binary one, etc). - APIs ==== +Keep in mind that each project (eg. FooBar) can have several releases +(eg. 1.1, 1.2, 1.3), and each of these releases can be provided in multiple +distributions (eg. a source distribution, a binary one, etc). + ReleaseInfo ------------ diff --git a/docs/source/projects-index.simple.rst b/docs/source/projects-index.simple.rst --- a/docs/source/projects-index.simple.rst +++ b/docs/source/projects-index.simple.rst @@ -27,39 +27,39 @@ Usage Exemples --------------- -To help you understand how using the `SimpleIndexCrawler` class, here are some basic +To help you understand how using the `Crawler` class, here are some basic usages. Request the simple index to get a specific distribution ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Supposing you want to scan an index to get a list of distributions for -the "foobar" project. You can use the "find" method for that. -The find method will browse the project page, and return :class:`ReleaseInfo` +the "foobar" project. You can use the "get_releases" method for that. +The get_releases method will browse the project page, and return :class:`ReleaseInfo` objects for each found link that rely on downloads. :: >>> from distutils2.index.simple import Crawler >>> crawler = Crawler() - >>> crawler.find("FooBar") + >>> crawler.get_releases("FooBar") [, ] Note that you also can request the client about specific versions, using version specifiers (described in `PEP 345 `_):: - >>> client.find("FooBar < 1.2") + >>> client.get_releases("FooBar < 1.2") [, ] -`find` returns a list of :class:`ReleaseInfo`, but you also can get the best -distribution that fullfil your requirements, using "get":: +`get_releases` returns a list of :class:`ReleaseInfo`, but you also can get the best +distribution that fullfil your requirements, using "get_release":: - >>> client.get("FooBar < 1.2") + >>> client.get_release("FooBar < 1.2") Download distributions +++++++++++++++++++++++ -As it can get the urls of distributions provided by PyPI, the `SimpleIndexCrawler` +As it can get the urls of distributions provided by PyPI, the `Crawler` client also can download the distributions and put it for you in a temporary destination:: @@ -74,7 +74,7 @@ While downloading, the md5 of the archive will be checked, if not matches, it will try another time, then if fails again, raise `MD5HashDoesNotMatchError`. -Internally, that's not the SimpleIndexCrawler which download the distributions, but the +Internally, that's not the Crawler which download the distributions, but the `DistributionInfo` class. Please refer to this documentation for more details. Following PyPI external links @@ -87,35 +87,45 @@ It's possible to tell the PyPIClient to follow external links by setting the `follow_externals` attribute, on instanciation or after:: - >>> client = SimpleIndexCrawler(follow_externals=True) + >>> client = Crawler(follow_externals=True) or :: - >>> client = SimpleIndexCrawler() + >>> client = Crawler() >>> client.follow_externals = True Working with external indexes, and mirrors +++++++++++++++++++++++++++++++++++++++++++ -The default `SimpleIndexCrawler` behavior is to rely on the Python Package index stored +The default `Crawler` behavior is to rely on the Python Package index stored on PyPI (http://pypi.python.org/simple). As you can need to work with a local index, or private indexes, you can specify it using the index_url parameter:: - >>> client = SimpleIndexCrawler(index_url="file://filesystem/path/") + >>> client = Crawler(index_url="file://filesystem/path/") or :: - >>> client = SimpleIndexCrawler(index_url="http://some.specific.url/") + >>> client = Crawler(index_url="http://some.specific.url/") You also can specify mirrors to fallback on in case the first index_url you provided doesnt respond, or not correctly. The default behavior for -`SimpleIndexCrawler` is to use the list provided by Python.org DNS records, as +`Crawler` is to use the list provided by Python.org DNS records, as described in the :pep:`381` about mirroring infrastructure. If you don't want to rely on these, you could specify the list of mirrors you want to try by specifying the `mirrors` attribute. It's a simple iterable:: >>> mirrors = ["http://first.mirror","http://second.mirror"] - >>> client = SimpleIndexCrawler(mirrors=mirrors) + >>> client = Crawler(mirrors=mirrors) + +Searching in the simple index ++++++++++++++++++++++++++++++ + +It's possible to search for projects with specific names in the package index. +Assuming you want to find all projects containing the "Grail" keyword:: + + >>> client.search(name="grail") + ["holy grail", "unholy grail", "grail"] + diff --git a/docs/source/projects-index.xmlrpc.rst b/docs/source/projects-index.xmlrpc.rst --- a/docs/source/projects-index.xmlrpc.rst +++ b/docs/source/projects-index.xmlrpc.rst @@ -49,19 +49,19 @@ most used way for users (eg. "give me the last version of the FooBar project"). This can be accomplished using the following syntax:: - >>> client = XMLRPCClient() - >>> client.get("Foobar (<= 1.3)) + >>> client = xmlrpc.Client() + >>> client.get_release("Foobar (<= 1.3)) - >>> client.find("FooBar (<= 1.3)") + >>> client.get_releases("FooBar (<= 1.3)") [FooBar 1.1, FooBar 1.1.1, FooBar 1.2, FooBar 1.2.1] And we also can find for specific fields:: - >>> client.find_by(field=value) + >>> client.search_projects(field=value) You could specify the operator to use, default is "or":: - >>> client.find_by(field=value, operator="and") + >>> client.search_projects(field=value, operator="and") The specific fields you can search are: @@ -85,7 +85,7 @@ XML-RPC is a prefered way to retrieve metadata informations from indexes. It's really simple to do so:: - >>> client = XMLRPCClient() + >>> client = xmlrpc.Client() >>> client.get_metadata("FooBar", "1.1") @@ -94,7 +94,7 @@ metadata:: >>> foobar11 = ReleaseInfo("FooBar", "1.1") - >>> client = XMLRPCClient() + >>> client = xmlrpc.Client() >>> returned_release = client.get_metadata(release=foobar11) >>> returned_release @@ -105,7 +105,7 @@ To retrieve all the releases for a project, you can build them using `get_releases`:: - >>> client = XMLRPCClient() + >>> client = xmlrpc.Client() >>> client.get_releases("FooBar") [, , ] @@ -119,7 +119,7 @@ It's possible to retrive informations about distributions, e.g "what are the existing distributions for this release ? How to retrieve them ?":: - >>> client = XMLRPCClient() + >>> client = xmlrpc.Client() >>> release = client.get_distributions("FooBar", "1.1") >>> release.dists {'sdist': , 'bdist': } @@ -140,7 +140,7 @@ :class:`distutils2.index.xmlrpc.Client` directly (they will be made, but they're invisible to the you):: - >>> client = XMLRPCClient() + >>> client = xmlrpc.Client() >>> releases = client.get_releases("FooBar") >>> releases.get_release("1.1").metadata diff --git a/src/distutils2/index/base.py b/src/distutils2/index/base.py --- a/src/distutils2/index/base.py +++ b/src/distutils2/index/base.py @@ -1,75 +1,13 @@ from distutils2.version import VersionPredicate -from distutils2.index.errors import DistributionNotFound +from distutils2.index.dist import ReleasesList -class IndexClient(object): - """Base class containing common index client methods""" +class BaseClient(object): + """Base class containing common methods for the index crawlers/clients""" - def _search_for_releases(self, requirements): - """To be redefined in child classes""" - return NotImplemented - - def find(self, requirements, prefer_final=None): - """Browse the PyPI to find distributions that fullfil the given - requirements. - - :param requirements: A project name and it's distribution, using - version specifiers, as described in PEP345. - :type requirements: You can pass either a version.VersionPredicate - or a string. - :param prefer_final: if the version is not mentioned in requirements, - and the last version is not a "final" one - (alpha, beta, etc.), pick up the last final - version. - """ - requirements = self._get_version_predicate(requirements) - prefer_final = self._get_prefer_final(prefer_final) - - # internally, rely on the "_search_for_release" method - dists = self._search_for_releases(requirements) - if dists: - dists = dists.filter(requirements) - dists.sort_releases(prefer_final=prefer_final) - return dists - - def get(self, requirements, prefer_final=None): - """Return only one release that fulfill the given requirements. - - :param requirements: A project name and it's distribution, using - version specifiers, as described in PEP345. - :type requirements: You can pass either a version.VersionPredicate - or a string. - :param prefer_final: if the version is not mentioned in requirements, - and the last version is not a "final" one - (alpha, beta, etc.), pick up the last final - version. - """ - predicate = self._get_version_predicate(requirements) - - # internally, rely on the "_get_release" method - dist = self._get_release(predicate, prefer_final=prefer_final) - if not dist: - raise DistributionNotFound(requirements) - return dist - - def download(self, requirements, temp_path=None, prefer_final=None, - prefer_source=True): - """Download the distribution, using the requirements. - - If more than one distribution match the requirements, use the last - version. - Download the distribution, and put it in the temp_path. If no temp_path - is given, creates and return one. - - Returns the complete absolute path to the downloaded archive. - - :param requirements: The same as the find attribute of `find`. - - You can specify prefer_final argument here. If not, the default - one will be used. - """ - return self.get(requirements, prefer_final)\ - .download(prefer_source=prefer_source, path=temp_path) + def __init__(self, prefer_final, prefer_source): + self._prefer_final = prefer_final + self._prefer_source = prefer_source def _get_version_predicate(self, requirements): """Return a VersionPredicate object, from a string or an already @@ -80,9 +18,37 @@ return requirements def _get_prefer_final(self, prefer_final=None): - """Return the prefer_final bit parameter or the specified one if - exists.""" + """Return the prefer_final internal parameter or the specified one if + provided""" if prefer_final: return prefer_final else: return self._prefer_final + + def _get_prefer_source(self, prefer_source=None): + """Return the prefer_source internal parameter or the specified one if + provided""" + if prefer_source: + return prefer_source + else: + return self._prefer_source + + def _get_project(self, project_name): + """Return an project instance, create it if necessary""" + return self._projects.setdefault(project_name, + ReleasesList(project_name)) + + def download_distribution(self, requirements, temp_path=None, + prefer_source=None, prefer_final=None): + """Download a distribution from the last release according to the + requirements. + + If temp_path is provided, download to this path, otherwise, create a + temporary location for the download and return it. + """ + prefer_final = self._get_prefer_final(prefer_final) + prefer_source = self._get_prefer_source(prefer_source) + release = self.get_release(requirements, prefer_final) + if release: + dist = release.get_distribution(prefer_source=prefer_source) + return dist.download(temp_path) diff --git a/src/distutils2/index/dist.py b/src/distutils2/index/dist.py --- a/src/distutils2/index/dist.py +++ b/src/distutils2/index/dist.py @@ -53,17 +53,17 @@ self.metadata = DistributionMetadata(mapping=metadata) self.dists = {} self.hidden = hidden - + if 'dist_type' in kwargs: dist_type = kwargs.pop('dist_type') self.add_distribution(dist_type, **kwargs) - + def set_version(self, version): try: self._version = NormalizedVersion(version) except IrrationalVersionError: suggestion = suggest_normalized_version(version) - if suggestion: + if suggestion: self.version = suggestion else: raise IrrationalVersionError(version) @@ -94,7 +94,7 @@ self.dists[dist_type].add_url(**params) else: self.dists[dist_type] = DistInfo(self, dist_type, **params) - + def get_distribution(self, dist_type=None, prefer_source=True): """Return a distribution. @@ -144,7 +144,7 @@ % (self.name, other.name)) def __repr__(self): - return "<%s %s>" %(self.name, self.version) + return "<%s %s>" % (self.name, self.version) def __eq__(self, other): self._check_is_comparable(other) @@ -307,7 +307,7 @@ def add_releases(self, releases): """Add releases in the release list. - + :param: releases is a list of ReleaseInfo objects. """ for r in releases: @@ -367,7 +367,7 @@ super(ReleasesList, self).sort( key=lambda i: [getattr(i, arg) for arg in sort_by], reverse=reverse, *args, **kwargs) - + def get_release(self, version): """Return a release from it's version. """ @@ -384,7 +384,7 @@ string = 'Project "%s"' % self.name if self.get_versions(): string += ' versions: %s' % ', '.join(self.get_versions()) - return '<%s>' % string + return '<%s>' % string def get_infos_from_url(url, probable_dist_name=None, is_external=True): @@ -454,7 +454,7 @@ version = archive_name.lstrip(name) else: name, version = eager_split(archive_name) - + version = suggest_normalized_version(version) if version is not None and name != "": return (name.lower(), version) diff --git a/src/distutils2/index/errors.py b/src/distutils2/index/errors.py --- a/src/distutils2/index/errors.py +++ b/src/distutils2/index/errors.py @@ -5,23 +5,27 @@ from distutils2.errors import DistutilsError -class IndexError(DistutilsError): +class IndexesError(DistutilsError): """The base class for errors of the index python package.""" -class ProjectNotFound(IndexError): +class ProjectNotFound(IndexesError): """Project has not been found""" -class DistributionNotFound(IndexError): - """No distribution match the given requirements.""" +class DistributionNotFound(IndexesError): + """The release has not been found""" -class CantParseArchiveName(IndexError): +class ReleaseNotFound(IndexesError): + """The release has not been found""" + + +class CantParseArchiveName(IndexesError): """An archive name can't be parsed to find distribution name and version""" -class DownloadError(IndexError): +class DownloadError(IndexesError): """An error has occurs while downloading""" @@ -29,13 +33,13 @@ """Compared hashes does not match""" -class UnsupportedHashName(IndexError): +class UnsupportedHashName(IndexesError): """A unsupported hashname has been used""" -class UnableToDownload(IndexError): +class UnableToDownload(IndexesError): """All mirrors have been tried, without success""" -class InvalidSearchField(IndexError): +class InvalidSearchField(IndexesError): """An invalid search field has been used""" diff --git a/src/distutils2/index/simple.py b/src/distutils2/index/simple.py --- a/src/distutils2/index/simple.py +++ b/src/distutils2/index/simple.py @@ -13,11 +13,12 @@ import urlparse import logging -from distutils2.index.base import IndexClient +from distutils2.index.base import BaseClient from distutils2.index.dist import (ReleasesList, EXTENSIONS, get_infos_from_url, MD5_HASH) -from distutils2.index.errors import (IndexError, DownloadError, - UnableToDownload, CantParseArchiveName) +from distutils2.index.errors import (IndexesError, DownloadError, + UnableToDownload, CantParseArchiveName, + ReleaseNotFound) from distutils2.index.mirrors import get_mirrors from distutils2 import __version__ as __distutils2_version__ @@ -55,6 +56,7 @@ return _socket_timeout return _socket_timeout + def with_mirror_support(): """Decorator that makes the mirroring support easier""" def wrapper(func): @@ -67,21 +69,27 @@ try: self._switch_to_next_mirror() except KeyError: - raise UnableToDownload("Tried all mirrors") + raise UnableToDownload("Tried all mirrors") else: self._mirrors_tries += 1 - self._releases.clear() + self._projects.clear() return wrapped(self, *args, **kwargs) return wrapped return wrapper -class Crawler(IndexClient): + +class Crawler(BaseClient): """Provides useful tools to request the Python Package Index simple API. You can specify both mirrors and mirrors_url, but mirrors_url will only be used if mirrors is set to None. :param index_url: the url of the simple index to search on. + :param prefer_final: if the version is not mentioned, and the last + version is not a "final" one (alpha, beta, etc.), + pick up the last final version. + :param prefer_source: if the distribution type is not mentioned, pick up + the source one if available. :param follow_externals: tell if following external links is needed or not. Default is False. :param hosts: a list of hosts allowed to be processed while using @@ -89,9 +97,6 @@ hosts. :param follow_externals: tell if following external links is needed or not. Default is False. - :param prefer_final: if the version is not mentioned, and the last - version is not a "final" one (alpha, beta, etc.), - pick up the last final version. :param mirrors_url: the url to look on for DNS records giving mirror adresses. :param mirrors: a list of mirrors (see PEP 381). @@ -100,12 +105,13 @@ on mirrors before switching. """ - def __init__(self, index_url=DEFAULT_INDEX_URL, hosts=DEFAULT_HOSTS, - follow_externals=False, prefer_final=False, - mirrors_url=None, mirrors=None, + def __init__(self, index_url=DEFAULT_INDEX_URL, prefer_final=False, + prefer_source=True, hosts=DEFAULT_HOSTS, + follow_externals=False, mirrors_url=None, mirrors=None, timeout=SOCKET_TIMEOUT, mirrors_max_tries=0): + super(Crawler, self).__init__(prefer_final, prefer_source) self.follow_externals = follow_externals - + # mirroring attributes. if not index_url.endswith("/"): index_url += "/" @@ -114,12 +120,10 @@ mirrors = get_mirrors(mirrors_url) self._mirrors = set(mirrors) self._mirrors_used = set() - self.index_url = index_url + self.index_url = index_url self._mirrors_max_tries = mirrors_max_tries self._mirrors_tries = 0 - self._timeout = timeout - self._prefer_final = prefer_final # create a regexp to match all given hosts self._allowed_hosts = re.compile('|'.join(map(translate, hosts))).match @@ -128,34 +132,44 @@ # scanning them multple time (eg. if there is multiple pages pointing # on one) self._processed_urls = [] - self._releases = {} - + self._projects = {} + @with_mirror_support() - def search(self, name=None, **kwargs): - """Search the index for projects containing the given name""" + def search_projects(self, name=None, **kwargs): + """Search the index for projects containing the given name. + + Return a list of names. + """ index = self._open_url(self.index_url) - projectname = re.compile("""]*>(.?[^<]*%s.?[^<]*)""" % name, flags=re.I) matching_projects = [] for match in projectname.finditer(index.read()): - matching_projects.append(match.group(1)) - + matching_projects.append(match.group(1)) return matching_projects - def _search_for_releases(self, requirements): - """Search for distributions and return a ReleaseList object containing - the results + def get_releases(self, requirements, prefer_final=None): + """Search for releases and return a ReleaseList object containing + the results. """ - # process the index page for the project name, searching for - # distributions. - self._process_index_page(requirements.name) - return self._releases.setdefault(requirements.name, - ReleasesList(requirements.name)) - - def _get_release(self, requirements, prefer_final): + predicate = self._get_version_predicate(requirements) + prefer_final = self._get_prefer_final(prefer_final) + self._process_index_page(predicate.name) + releases = self._projects.setdefault(predicate.name, + ReleasesList(predicate.name)) + if releases: + releases = releases.filter(predicate) + releases.sort_releases(prefer_final=prefer_final) + return releases + + def get_release(self, requirements, prefer_final=None): """Return only one release that fulfill the given requirements""" - return self.find(requirements, prefer_final).get_last(requirements) + predicate = self._get_version_predicate(requirements) + release = self.get_releases(predicate, prefer_final)\ + .get_last(predicate) + if not release: + raise ReleaseNotFound("No release matches the given criterias") + return release def _switch_to_next_mirror(self): """Switch to the next mirror (eg. point self.index_url to the next @@ -216,18 +230,18 @@ name = release.name else: name = release_info['name'] - if not name in self._releases: - self._releases[name] = ReleasesList(name) + if not name in self._projects: + self._projects[name] = ReleasesList(name) if release: - self._releases[name].add_release(release=release) + self._projects[name].add_release(release=release) else: name = release_info.pop('name') version = release_info.pop('version') dist_type = release_info.pop('dist_type') - self._releases[name].add_release(version, dist_type, + self._projects[name].add_release(version, dist_type, **release_info) - return self._releases[name] + return self._projects[name] def _process_url(self, url, project_name=None, follow_links=True): """Process an url and search for distributions packages. @@ -256,7 +270,7 @@ infos = get_infos_from_url(link, project_name, is_external=not self.index_url in url) except CantParseArchiveName, e: - logging.warning("version has not been parsed: %s" + logging.warning("version has not been parsed: %s" % e) else: self._register_release(release_info=infos) @@ -324,7 +338,7 @@ """ scheme, netloc, path, params, query, frag = urlparse.urlparse(url) - + # authentication stuff if scheme in ('http', 'https'): auth, host = urllib2.splituser(netloc) @@ -335,7 +349,7 @@ if scheme == 'file': if url.endswith('/'): url += "index.html" - + # add authorization headers if auth is provided if auth: auth = "Basic " + \ @@ -351,7 +365,7 @@ fp = urllib2.urlopen(request) except (ValueError, httplib.InvalidURL), v: msg = ' '.join([str(arg) for arg in v.args]) - raise IndexError('%s %s' % (url, msg)) + raise IndexesError('%s %s' % (url, msg)) except urllib2.HTTPError, v: return v except urllib2.URLError, v: @@ -372,7 +386,6 @@ if s2 == scheme and h2 == host: fp.url = urlparse.urlunparse( (s2, netloc, path2, param2, query2, frag2)) - return fp def _decode_entity(self, match): diff --git a/src/distutils2/index/xmlrpc.py b/src/distutils2/index/xmlrpc.py --- a/src/distutils2/index/xmlrpc.py +++ b/src/distutils2/index/xmlrpc.py @@ -2,9 +2,9 @@ import xmlrpclib from distutils2.errors import IrrationalVersionError -from distutils2.index.base import IndexClient +from distutils2.index.base import BaseClient from distutils2.index.errors import ProjectNotFound, InvalidSearchField -from distutils2.index.dist import ReleaseInfo, ReleasesList +from distutils2.index.dist import ReleaseInfo PYPI_XML_RPC_URL = 'http://python.org/pypi' @@ -14,9 +14,9 @@ 'description', 'keywords', 'platform', 'download_url'] -class Client(IndexClient): +class Client(BaseClient): """Client to query indexes using XML-RPC method calls. - + If no server_url is specified, use the default PyPI XML-RPC URL, defined in the PYPI_XML_RPC_URL constant:: @@ -29,43 +29,26 @@ 'http://someurl/' """ - def __init__(self, server_url=PYPI_XML_RPC_URL, prefer_final=False): + def __init__(self, server_url=PYPI_XML_RPC_URL, prefer_final=False, + prefer_source=True): + super(Client, self).__init__(prefer_final, prefer_source) self.server_url = server_url self._projects = {} - self._prefer_final = prefer_final - def _search_for_releases(self, requirements): - return self.get_releases(requirements.name) - - def _get_release(self, requirements, prefer_final=False): - releases = self.get_releases(requirements.name) - release = releases.get_last(requirements, prefer_final) + def get_release(self, requirements, prefer_final=False): + """Return a release with all complete metadata and distribution + related informations. + """ + prefer_final = self._get_prefer_final(prefer_final) + predicate = self._get_version_predicate(requirements) + releases = self.get_releases(predicate.name) + release = releases.get_last(predicate, prefer_final) self.get_metadata(release.name, "%s" % release.version) self.get_distributions(release.name, "%s" % release.version) return release - @property - def proxy(self): - """Property used to return the XMLRPC server proxy. - - If no server proxy is defined yet, creates a new one:: - - >>> client = XmlRpcClient() - >>> client.proxy() - - - """ - if not hasattr(self, '_server_proxy'): - self._server_proxy = xmlrpclib.ServerProxy(self.server_url) - - return self._server_proxy - - def _get_project(self, project_name): - """Return an project instance, create it if necessary""" - return self._projects.setdefault(project_name, - ReleasesList(project_name)) - - def get_releases(self, project_name, show_hidden=True, force_update=False): + def get_releases(self, requirements, prefer_final=None, show_hidden=True, + force_update=False): """Return the list of existing releases for a specific project. Cache the results from one call to another. @@ -88,6 +71,9 @@ def get_versions(project_name, show_hidden): return self.proxy.package_releases(project_name, show_hidden) + predicate = self._get_version_predicate(requirements) + prefer_final = self._get_prefer_final(prefer_final) + project_name = predicate.name if not force_update and (project_name in self._projects): project = self._projects[project_name] if not project.contains_hidden and show_hidden: @@ -100,15 +86,17 @@ for version in hidden_versions: project.add_release(release=ReleaseInfo(project_name, version)) - return project else: versions = get_versions(project_name, show_hidden) if not versions: raise ProjectNotFound(project_name) project = self._get_project(project_name) - project.add_releases([ReleaseInfo(project_name, version) + project.add_releases([ReleaseInfo(project_name, version) for version in versions]) - return project + project = project.filter(predicate) + project.sort_releases(prefer_final) + return project + def get_distributions(self, project_name, version): """Grab informations about distributions from XML-RPC. @@ -143,9 +131,9 @@ release.set_metadata(metadata) return release - def search(self, name=None, operator="or", **kwargs): + def search_projects(self, name=None, operator="or", **kwargs): """Find using the keys provided in kwargs. - + You can set operator to "and" or "or". """ for key in kwargs: @@ -157,9 +145,25 @@ for p in projects: project = self._get_project(p['name']) try: - project.add_release(release=ReleaseInfo(p['name'], - p['version'], metadata={'summary':p['summary']})) + project.add_release(release=ReleaseInfo(p['name'], + p['version'], metadata={'summary': p['summary']})) except IrrationalVersionError, e: logging.warn("Irrational version error found: %s" % e) - + return [self._projects[p['name']] for p in projects] + + @property + def proxy(self): + """Property used to return the XMLRPC server proxy. + + If no server proxy is defined yet, creates a new one:: + + >>> client = XmlRpcClient() + >>> client.proxy() + + + """ + if not hasattr(self, '_server_proxy'): + self._server_proxy = xmlrpclib.ServerProxy(self.server_url) + + return self._server_proxy diff --git a/src/distutils2/tests/test_index_simple.py b/src/distutils2/tests/test_index_simple.py --- a/src/distutils2/tests/test_index_simple.py +++ b/src/distutils2/tests/test_index_simple.py @@ -85,14 +85,14 @@ # Browse the index, asking for a specified release version # The PyPI index contains links for version 1.0, 1.1, 2.0 and 2.0.1 crawler = self._get_simple_crawler(server) - last_release = crawler.get("foobar") + last_release = crawler.get_release("foobar") # we have scanned the index page self.assertIn(server.full_address + "/simple/foobar/", crawler._processed_urls) # we have found 4 releases in this page - self.assertEqual(len(crawler._releases["foobar"]), 4) + self.assertEqual(len(crawler._projects["foobar"]), 4) # and returned the most recent one self.assertEqual("%s" % last_release.version, '2.0.1') @@ -140,7 +140,7 @@ # Try to request the package index, wich contains links to "externals" # resources. They have to be scanned too. crawler = self._get_simple_crawler(server, follow_externals=True) - crawler.get("foobar") + crawler.get_release("foobar") self.assertIn(server.full_address + "/external/external.html", crawler._processed_urls) @@ -150,7 +150,7 @@ # Test that telling the simple pyPI client to not retrieve external # works crawler = self._get_simple_crawler(server, follow_externals=False) - crawler.get("foobar") + crawler.get_release("foobar") self.assertNotIn(server.full_address + "/external/external.html", crawler._processed_urls) @@ -175,7 +175,7 @@ # scan a test index crawler = Crawler(index_url, follow_externals=True) - releases = crawler.find("foobar") + releases = crawler.get_releases("foobar") server.stop() # we have only one link, because links are compared without md5 @@ -196,7 +196,7 @@ # process the pages crawler = self._get_simple_crawler(server, follow_externals=True) - crawler.find("foobar") + crawler.get_releases("foobar") # now it should have processed only pages with links rel="download" # and rel="homepage" self.assertIn("%s/simple/foobar/" % server.full_address, @@ -225,7 +225,7 @@ mirrors=[mirror.full_address, ]) # this should not raise a timeout - self.assertEqual(4, len(crawler.find("foo"))) + self.assertEqual(4, len(crawler.get_releases("foo"))) finally: mirror.stop() @@ -274,7 +274,7 @@ index_path = os.sep.join(["file://" + PYPI_DEFAULT_STATIC_PATH, "test_found_links", "simple"]) crawler = Crawler(index_path) - dists = crawler.find("foobar") + dists = crawler.get_releases("foobar") self.assertEqual(4, len(dists)) def test_get_link_matcher(self): @@ -301,11 +301,11 @@ self.assertIn('http://example.org/some/download', found_links) @use_pypi_server("project_list") - def test_search(self, server): + def test_search_projects(self, server): # we can search the index for some projects, on their names # the case used no matters here crawler = self._get_simple_crawler(server) - projects = crawler.search("Foobar") + projects = crawler.search_projects("Foobar") self.assertListEqual(['FooBar-bar', 'Foobar-baz', 'Baz-FooBar'], projects) diff --git a/src/distutils2/tests/test_index_xmlrpc.py b/src/distutils2/tests/test_index_xmlrpc.py --- a/src/distutils2/tests/test_index_xmlrpc.py +++ b/src/distutils2/tests/test_index_xmlrpc.py @@ -11,22 +11,22 @@ return Client(server.full_address, *args, **kwargs) @use_xmlrpc_server() - def test_search(self, server): - # test that the find method return a list of ReleasesList + def test_search_projects(self, server): client = self._get_client(server) server.xmlrpc.set_search_result(['FooBar', 'Foo', 'FooFoo']) - results = [r.name for r in client.search(name='Foo')] + results = [r.name for r in client.search_projects(name='Foo')] self.assertEqual(3, len(results)) self.assertIn('FooBar', results) self.assertIn('Foo', results) self.assertIn('FooFoo', results) - def test_search_bad_fields(self): + def test_search_projects_bad_fields(self): client = Client() - self.assertRaises(InvalidSearchField, client.search, invalid="test") + self.assertRaises(InvalidSearchField, client.search_projects, + invalid="test") @use_xmlrpc_server() - def test_find(self, server): + def test_get_releases(self, server): client = self._get_client(server) server.xmlrpc.set_distributions([ {'name': 'FooBar', 'version': '1.1'}, @@ -37,7 +37,7 @@ # use a lambda here to avoid an useless mock call server.xmlrpc.list_releases = lambda *a, **k: ['1.1', '1.2', '1.3'] - releases = client.find('FooBar (<=1.2)') + releases = client.get_releases('FooBar (<=1.2)') # dont call release_data and release_url; just return name and version. self.assertEqual(2, len(releases)) versions = releases.get_versions() @@ -45,30 +45,7 @@ self.assertIn('1.2', versions) self.assertNotIn('1.3', versions) - self.assertRaises(ProjectNotFound, client.find,'Foo') - - @use_xmlrpc_server() - def test_get_releases(self, server): - client = self._get_client(server) - server.xmlrpc.set_distributions([ - {'name':'FooBar', 'version': '0.8', 'hidden': True}, - {'name':'FooBar', 'version': '0.9', 'hidden': True}, - {'name':'FooBar', 'version': '1.1'}, - {'name':'FooBar', 'version': '1.2'}, - ]) - releases = client.get_releases('FooBar', False) - versions = releases.get_versions() - self.assertEqual(2, len(versions)) - self.assertIn('1.1', versions) - self.assertIn('1.2', versions) - - releases2 = client.get_releases('FooBar', True) - versions = releases2.get_versions() - self.assertEqual(4, len(versions)) - self.assertIn('0.8', versions) - self.assertIn('0.9', versions) - self.assertIn('1.1', versions) - self.assertIn('1.2', versions) + self.assertRaises(ProjectNotFound, client.get_releases,'Foo') @use_xmlrpc_server() def test_get_distributions(self, server): -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:46 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:46 +0200 Subject: [Python-checkins] distutils2: Fix some lower/upper case issues about the index crawlers. Message-ID: tarek.ziade pushed 3d4ce44d5201 to distutils2: http://hg.python.org/distutils2/rev/3d4ce44d5201 changeset: 462:3d4ce44d5201 user: Alexis Metaireau date: Tue Jul 27 16:33:06 2010 +0200 summary: Fix some lower/upper case issues about the index crawlers. files: src/distutils2/index/base.py, src/distutils2/index/dist.py, src/distutils2/index/simple.py, src/distutils2/index/xmlrpc.py diff --git a/src/distutils2/index/base.py b/src/distutils2/index/base.py --- a/src/distutils2/index/base.py +++ b/src/distutils2/index/base.py @@ -35,7 +35,7 @@ def _get_project(self, project_name): """Return an project instance, create it if necessary""" - return self._projects.setdefault(project_name, + return self._projects.setdefault(project_name.lower(), ReleasesList(project_name)) def download_distribution(self, requirements, temp_path=None, diff --git a/src/distutils2/index/dist.py b/src/distutils2/index/dist.py --- a/src/distutils2/index/dist.py +++ b/src/distutils2/index/dist.py @@ -357,7 +357,7 @@ distribution creation (eg. the arguments of the DistInfo constructor). """ if release: - if release.name != self.name: + if release.name.lower() != self.name.lower(): raise ValueError(release.name) version = '%s' % release.version if not version in self.get_versions(): diff --git a/src/distutils2/index/simple.py b/src/distutils2/index/simple.py --- a/src/distutils2/index/simple.py +++ b/src/distutils2/index/simple.py @@ -158,10 +158,10 @@ prefer_final = self._get_prefer_final(prefer_final) self._process_index_page(predicate.name) - if not self._projects.has_key(predicate.name): + if not self._projects.has_key(predicate.name.lower()): raise ProjectNotFound() - releases = self._projects.get(predicate) + releases = self._projects.get(predicate.name.lower()) releases.sort_releases(prefer_final=prefer_final) return releases @@ -233,18 +233,18 @@ name = release.name else: name = release_info['name'] - if not name in self._projects: - self._projects[name] = ReleasesList(name) + if not name.lower() in self._projects: + self._projects[name.lower()] = ReleasesList(name) if release: - self._projects[name].add_release(release=release) + self._projects[name.lower()].add_release(release=release) else: name = release_info.pop('name') version = release_info.pop('version') dist_type = release_info.pop('dist_type') - self._projects[name].add_release(version, dist_type, - **release_info) - return self._projects[name] + self._projects[name.lower()].add_release(version, dist_type, + **release_info) + return self._projects[name.lower()] def _process_url(self, url, project_name=None, follow_links=True): """Process an url and search for distributions packages. diff --git a/src/distutils2/index/xmlrpc.py b/src/distutils2/index/xmlrpc.py --- a/src/distutils2/index/xmlrpc.py +++ b/src/distutils2/index/xmlrpc.py @@ -75,8 +75,8 @@ predicate = self._get_version_predicate(requirements) prefer_final = self._get_prefer_final(prefer_final) project_name = predicate.name - if not force_update and (project_name in self._projects): - project = self._projects[project_name] + if not force_update and (project_name.lower() in self._projects): + project = self._projects[project_name.lower()] if not project.contains_hidden and show_hidden: # if hidden releases are requested, and have an existing # list of releases that does not contains hidden ones @@ -151,7 +151,7 @@ except IrrationalVersionError, e: logging.warn("Irrational version error found: %s" % e) - return [self._projects[p['name']] for p in projects] + return [self._projects[p['name'].lower()] for p in projects] @property def proxy(self): -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:46 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:46 +0200 Subject: [Python-checkins] distutils2: Typo Message-ID: tarek.ziade pushed 06cb3de6c55a to distutils2: http://hg.python.org/distutils2/rev/06cb3de6c55a changeset: 471:06cb3de6c55a user: Alexis Metaireau date: Thu Jul 29 19:26:05 2010 +0200 summary: Typo files: src/distutils2/tests/pypi_server.py diff --git a/src/distutils2/tests/pypi_server.py b/src/distutils2/tests/pypi_server.py --- a/src/distutils2/tests/pypi_server.py +++ b/src/distutils2/tests/pypi_server.py @@ -235,7 +235,7 @@ class MockDist(object): """Fake distribution, used in the Mock PyPI Server""" def __init__(self, name, version="1.0", hidden=False, url="http://url/", - type="source", filename="", size=10000, + type="sdist", filename="", size=10000, digest="123456", downloads=7, has_sig=False, python_version="source", comment="comment", author="John Doe", author_email="john at doe.name", -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:46 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:46 +0200 Subject: [Python-checkins] distutils2: Add cache support for unpack Message-ID: tarek.ziade pushed 981897e28aca to distutils2: http://hg.python.org/distutils2/rev/981897e28aca changeset: 469:981897e28aca user: Alexis Metaireau date: Thu Jul 29 17:17:31 2010 +0200 summary: Add cache support for unpack files: src/distutils2/index/dist.py diff --git a/src/distutils2/index/dist.py b/src/distutils2/index/dist.py --- a/src/distutils2/index/dist.py +++ b/src/distutils2/index/dist.py @@ -229,6 +229,7 @@ self.release = release self.dist_type = dist_type self.python_version = python_version + self._unpacked_dir = None # set the downloaded path to None by default. The goal here # is to not download distributions multiple times self.downloaded_location = None @@ -306,22 +307,24 @@ Returns the location of the extracted files (root). """ - if path is None: - path = tempfile.mkdtemp() - - filename = self.download() - content_type = mimetypes.guess_type(filename)[0] - - if (content_type == 'application/zip' - or filename.endswith('.zip') - or filename.endswith('.pybundle') - or zipfile.is_zipfile(filename)): - unzip_file(filename, path, flatten=not filename.endswith('.pybundle')) - elif (content_type == 'application/x-gzip' - or tarfile.is_tarfile(filename) - or splitext(filename)[1].lower() in ('.tar', '.tar.gz', '.tar.bz2', '.tgz', '.tbz')): - untar_file(filename, path) - return path + if not self._unpacked_dir: + if path is None: + path = tempfile.mkdtemp() + + filename = self.download() + content_type = mimetypes.guess_type(filename)[0] + + if (content_type == 'application/zip' + or filename.endswith('.zip') + or filename.endswith('.pybundle') + or zipfile.is_zipfile(filename)): + unzip_file(filename, path, flatten=not filename.endswith('.pybundle')) + elif (content_type == 'application/x-gzip' + or tarfile.is_tarfile(filename) + or splitext(filename)[1].lower() in ('.tar', '.tar.gz', '.tar.bz2', '.tgz', '.tbz')): + untar_file(filename, path) + self._unpacked_dir = path + return self._unpacked_dir def _check_md5(self, filename): """Check that the md5 checksum of the given file matches the one in -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:46 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:46 +0200 Subject: [Python-checkins] distutils2: Return a list of ReleaseInfo instead of just names while searching the index. Message-ID: tarek.ziade pushed 5d3a2ef3b714 to distutils2: http://hg.python.org/distutils2/rev/5d3a2ef3b714 changeset: 465:5d3a2ef3b714 user: Alexis Metaireau date: Tue Jul 27 18:33:41 2010 +0200 summary: Return a list of ReleaseInfo instead of just names while searching the index. files: src/distutils2/index/simple.py, src/distutils2/tests/test_index_simple.py diff --git a/src/distutils2/index/simple.py b/src/distutils2/index/simple.py --- a/src/distutils2/index/simple.py +++ b/src/distutils2/index/simple.py @@ -147,7 +147,8 @@ flags=re.I) matching_projects = [] for match in projectname.finditer(index.read()): - matching_projects.append(match.group(1)) + project_name = match.group(1) + matching_projects.append(self._get_project(project_name)) return matching_projects def get_releases(self, requirements, prefer_final=None): diff --git a/src/distutils2/tests/test_index_simple.py b/src/distutils2/tests/test_index_simple.py --- a/src/distutils2/tests/test_index_simple.py +++ b/src/distutils2/tests/test_index_simple.py @@ -305,7 +305,7 @@ # we can search the index for some projects, on their names # the case used no matters here crawler = self._get_simple_crawler(server) - projects = crawler.search_projects("Foobar") + projects = [p.name for p in crawler.search_projects("Foobar")] self.assertListEqual(['FooBar-bar', 'Foobar-baz', 'Baz-FooBar'], projects) -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:46 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:46 +0200 Subject: [Python-checkins] distutils2: Fix about how metadatas are set in ReleaseInfo Message-ID: tarek.ziade pushed 8b157764bbb8 to distutils2: http://hg.python.org/distutils2/rev/8b157764bbb8 changeset: 467:8b157764bbb8 user: Alexis Metaireau date: Tue Jul 27 18:53:14 2010 +0200 summary: Fix about how metadatas are set in ReleaseInfo files: src/distutils2/index/dist.py diff --git a/src/distutils2/index/dist.py b/src/distutils2/index/dist.py --- a/src/distutils2/index/dist.py +++ b/src/distutils2/index/dist.py @@ -153,6 +153,8 @@ .download(path=temp_path) def set_metadata(self, metadata): + if not self._metadata: + self._metadata = DistributionMetadata() self._metadata.update(metadata) def __getitem__(self, item): -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:46 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:46 +0200 Subject: [Python-checkins] distutils2: Add some cache support to the simple index crawler. Message-ID: tarek.ziade pushed 82ff0a16efe3 to distutils2: http://hg.python.org/distutils2/rev/82ff0a16efe3 changeset: 466:82ff0a16efe3 user: Alexis Metaireau date: Tue Jul 27 18:34:13 2010 +0200 summary: Add some cache support to the simple index crawler. files: src/distutils2/index/simple.py diff --git a/src/distutils2/index/simple.py b/src/distutils2/index/simple.py --- a/src/distutils2/index/simple.py +++ b/src/distutils2/index/simple.py @@ -151,11 +151,14 @@ matching_projects.append(self._get_project(project_name)) return matching_projects - def get_releases(self, requirements, prefer_final=None): + def get_releases(self, requirements, prefer_final=None, + force_update=False): """Search for releases and return a ReleaseList object containing the results. """ predicate = self._get_version_predicate(requirements) + if self._projects.has_key(predicate.name.lower()) and not force_update: + return self._projects.get(predicate.name.lower()) prefer_final = self._get_prefer_final(prefer_final) self._process_index_page(predicate.name) -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:46 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:46 +0200 Subject: [Python-checkins] distutils2: Add support of python_version in distributions for crawlers. Message-ID: tarek.ziade pushed 3095c06eded0 to distutils2: http://hg.python.org/distutils2/rev/3095c06eded0 changeset: 463:3095c06eded0 user: Alexis Metaireau date: Tue Jul 27 16:45:40 2010 +0200 summary: Add support of python_version in distributions for crawlers. files: src/distutils2/index/dist.py, src/distutils2/index/xmlrpc.py, src/distutils2/tests/test_index_xmlrpc.py diff --git a/src/distutils2/index/dist.py b/src/distutils2/index/dist.py --- a/src/distutils2/index/dist.py +++ b/src/distutils2/index/dist.py @@ -84,7 +84,7 @@ """proxy to version.is_final""" return self.version.is_final - def add_distribution(self, dist_type='sdist', **params): + def add_distribution(self, dist_type='sdist', python_version=None, **params): """Add distribution informations to this release. If distribution information is already set for this distribution type, add the given url paths to the distribution. This can be useful while @@ -100,6 +100,8 @@ self.dists[dist_type].add_url(**params) else: self.dists[dist_type] = DistInfo(self, dist_type, **params) + if python_version: + self.dists[dist_type].python_version = python_version def get_distribution(self, dist_type=None, prefer_source=True): """Return a distribution. @@ -181,7 +183,7 @@ """ def __init__(self, release, dist_type=None, url=None, hashname=None, - hashval=None, is_external=True): + hashval=None, is_external=True, python_version=None): """Create a new instance of DistInfo. :param release: a DistInfo class is relative to a release. @@ -196,6 +198,7 @@ """ self.release = release self.dist_type = dist_type + self.python_version = python_version # set the downloaded path to None by default. The goal here # is to not download distributions multiple times self.downloaded_location = None diff --git a/src/distutils2/index/xmlrpc.py b/src/distutils2/index/xmlrpc.py --- a/src/distutils2/index/xmlrpc.py +++ b/src/distutils2/index/xmlrpc.py @@ -115,7 +115,8 @@ dist_infos = {'url': info['url'], 'hashval': info['md5_digest'], 'hashname': 'md5', - 'is_external': False} + 'is_external': False, + 'python_version': info['python_version']} release.add_distribution(packagetype, **dist_infos) return release diff --git a/src/distutils2/tests/test_index_xmlrpc.py b/src/distutils2/tests/test_index_xmlrpc.py --- a/src/distutils2/tests/test_index_xmlrpc.py +++ b/src/distutils2/tests/test_index_xmlrpc.py @@ -53,7 +53,7 @@ server.xmlrpc.set_distributions([ {'name':'FooBar', 'version': '1.1', 'url': 'http://example.org/foobar-1.1-sdist.tar.gz', - 'digest': '1234567', 'type': 'sdist'}, + 'digest': '1234567', 'type': 'sdist', 'python_version':'source'}, {'name':'FooBar', 'version': '1.1', 'url': 'http://example.org/foobar-1.1-bdist.tar.gz', 'digest': '8912345', 'type': 'bdist'}, @@ -66,6 +66,7 @@ release['sdist'].url['url']) self.assertTrue('http://example.org/foobar-1.1-bdist.tar.gz', release['bdist'].url['url']) + self.assertEqual(release['sdist'].python_version, 'source') @use_xmlrpc_server() def test_get_metadata(self, server): -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:46 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:46 +0200 Subject: [Python-checkins] distutils2: Add a way to get the metadatas by downloading and opening archives. Message-ID: tarek.ziade pushed f314cf0ce672 to distutils2: http://hg.python.org/distutils2/rev/f314cf0ce672 changeset: 464:f314cf0ce672 user: Alexis Metaireau date: Tue Jul 27 18:08:46 2010 +0200 summary: Add a way to get the metadatas by downloading and opening archives. files: src/distutils2/index/dist.py, src/distutils2/index/simple.py diff --git a/src/distutils2/index/dist.py b/src/distutils2/index/dist.py --- a/src/distutils2/index/dist.py +++ b/src/distutils2/index/dist.py @@ -17,6 +17,7 @@ import urllib import urlparse import zipfile +import os try: import hashlib @@ -39,7 +40,7 @@ class ReleaseInfo(object): """Represent a release of a project (a project with a specific version). - The release contain the metadata informations related to this specific + The release contain the _metadata informations related to this specific version, and is also a container for distribution related informations. See the DistInfo class for more information about distributions. @@ -56,7 +57,10 @@ self.name = name self._version = None self.version = version - self.metadata = DistributionMetadata(mapping=metadata) + if metadata: + self._metadata = DistributionMetadata(mapping=metadata) + else: + self._metadata = None self.dists = {} self.hidden = hidden @@ -79,6 +83,18 @@ version = property(get_version, set_version) + def _set_metadata(self, unpack=True): + """Set the metadatas, using the archive if needed""" + location = self.get_distribution().unpack() + pkg_info = os.path.join(location, 'PKG-INFO') + self._metadata = DistributionMetadata(pkg_info) + + @property + def metadata(self): + if not self._metadata: + self._set_metadata() + return self._metadata + @property def is_final(self): """proxy to version.is_final""" @@ -137,7 +153,7 @@ .download(path=temp_path) def set_metadata(self, metadata): - self.metadata.update(metadata) + self._metadata.update(metadata) def __getitem__(self, item): """distributions are available using release["sdist"]""" @@ -326,8 +342,7 @@ """Filter and return a subset of releases matching the given predicate. """ return ReleasesList(self.name, [release for release in self - if release.name == predicate.name - and predicate.match(release.version)]) + if predicate.match(release.version)]) def get_last(self, predicate, prefer_final=None): """Return the "last" release, that satisfy the given predicates. diff --git a/src/distutils2/index/simple.py b/src/distutils2/index/simple.py --- a/src/distutils2/index/simple.py +++ b/src/distutils2/index/simple.py @@ -160,7 +160,7 @@ if not self._projects.has_key(predicate.name.lower()): raise ProjectNotFound() - + releases = self._projects.get(predicate.name.lower()) releases.sort_releases(prefer_final=prefer_final) return releases -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:46 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:46 +0200 Subject: [Python-checkins] distutils2: Add magic lazyloading for releases and distributions. Message-ID: tarek.ziade pushed 5aaf48db95aa to distutils2: http://hg.python.org/distutils2/rev/5aaf48db95aa changeset: 468:5aaf48db95aa user: Alexis Metaireau date: Wed Jul 28 17:52:33 2010 +0200 summary: Add magic lazyloading for releases and distributions. files: src/distutils2/index/base.py, src/distutils2/index/dist.py, src/distutils2/index/simple.py, src/distutils2/index/wrapper.py, src/distutils2/index/xmlrpc.py diff --git a/src/distutils2/index/base.py b/src/distutils2/index/base.py --- a/src/distutils2/index/base.py +++ b/src/distutils2/index/base.py @@ -8,6 +8,7 @@ def __init__(self, prefer_final, prefer_source): self._prefer_final = prefer_final self._prefer_source = prefer_source + self._index = self def _get_version_predicate(self, requirements): """Return a VersionPredicate object, from a string or an already @@ -36,7 +37,7 @@ def _get_project(self, project_name): """Return an project instance, create it if necessary""" return self._projects.setdefault(project_name.lower(), - ReleasesList(project_name)) + ReleasesList(project_name, index=self._index)) def download_distribution(self, requirements, temp_path=None, prefer_source=None, prefer_final=None): diff --git a/src/distutils2/index/dist.py b/src/distutils2/index/dist.py --- a/src/distutils2/index/dist.py +++ b/src/distutils2/index/dist.py @@ -17,7 +17,6 @@ import urllib import urlparse import zipfile -import os try: import hashlib @@ -38,7 +37,12 @@ DIST_TYPES = ['bdist', 'sdist'] -class ReleaseInfo(object): +class IndexReference(object): + def set_index(self, index=None): + self._index = index + + +class ReleaseInfo(IndexReference): """Represent a release of a project (a project with a specific version). The release contain the _metadata informations related to this specific version, and is also a container for distribution related informations. @@ -46,7 +50,8 @@ See the DistInfo class for more information about distributions. """ - def __init__(self, name, version, metadata=None, hidden=False, **kwargs): + def __init__(self, name, version, metadata=None, hidden=False, + index=None, **kwargs): """ :param name: the name of the distribution :param version: the version of the distribution @@ -54,6 +59,7 @@ :type metadata: dict :param kwargs: optional arguments for a new distribution. """ + self.set_index(index) self.name = name self._version = None self.version = version @@ -61,7 +67,7 @@ self._metadata = DistributionMetadata(mapping=metadata) else: self._metadata = None - self.dists = {} + self._dists = {} self.hidden = hidden if 'dist_type' in kwargs: @@ -83,22 +89,25 @@ version = property(get_version, set_version) - def _set_metadata(self, unpack=True): - """Set the metadatas, using the archive if needed""" - location = self.get_distribution().unpack() - pkg_info = os.path.join(location, 'PKG-INFO') - self._metadata = DistributionMetadata(pkg_info) - @property def metadata(self): + """If the metadata is not set, use the indexes to get it""" if not self._metadata: - self._set_metadata() + self._index.get_metadata(self.name, '%s' % self.version) return self._metadata @property def is_final(self): """proxy to version.is_final""" return self.version.is_final + + @property + def dists(self): + if self._dists is None: + self._index.get_distributions(self.name, '%s' % self.version) + if self._dists is None: + self._dists = {} + return self._dists def add_distribution(self, dist_type='sdist', python_version=None, **params): """Add distribution informations to this release. @@ -113,11 +122,12 @@ if dist_type not in DIST_TYPES: raise ValueError(dist_type) if dist_type in self.dists: - self.dists[dist_type].add_url(**params) + self._dists[dist_type].add_url(**params) else: - self.dists[dist_type] = DistInfo(self, dist_type, **params) + self._dists[dist_type] = DistInfo(self, dist_type, + index=self._index, **params) if python_version: - self.dists[dist_type].python_version = python_version + self._dists[dist_type].python_version = python_version def get_distribution(self, dist_type=None, prefer_source=True): """Return a distribution. @@ -196,12 +206,13 @@ __hash__ = object.__hash__ -class DistInfo(object): +class DistInfo(IndexReference): """Represents a distribution retrieved from an index (sdist, bdist, ...) """ def __init__(self, release, dist_type=None, url=None, hashname=None, - hashval=None, is_external=True, python_version=None): + hashval=None, is_external=True, python_version=None, + index=None): """Create a new instance of DistInfo. :param release: a DistInfo class is relative to a release. @@ -214,6 +225,7 @@ an index browsing, or from an external resource. """ + self.set_index(index) self.release = release self.dist_type = dist_type self.python_version = python_version @@ -329,22 +341,35 @@ self.release.name, self.release.version, self.dist_type or "") -class ReleasesList(list): +class ReleasesList(IndexReference): """A container of Release. Provides useful methods and facilities to sort and filter releases. """ - def __init__(self, name, releases=[], contains_hidden=False): - super(ReleasesList, self).__init__() + def __init__(self, name, releases=None, contains_hidden=False, index=None): + self.set_index(index) + self._releases = [] self.name = name self.contains_hidden = contains_hidden - self.add_releases(releases) + if releases: + self.add_releases(releases) + + @property + def releases(self): + if not self._releases: + self.fetch_releases() + return self._releases + + def fetch_releases(self): + self._index.get_releases(self.name) + return self.releases def filter(self, predicate): """Filter and return a subset of releases matching the given predicate. """ - return ReleasesList(self.name, [release for release in self - if predicate.match(release.version)]) + return ReleasesList(self.name, [release for release in self.releases + if predicate.match(release.version)], + index=self._index) def get_last(self, predicate, prefer_final=None): """Return the "last" release, that satisfy the given predicates. @@ -378,20 +403,22 @@ """ if release: if release.name.lower() != self.name.lower(): - raise ValueError(release.name) + raise ValueError("%s is not the same project than %s" % + (release.name, self.name)) version = '%s' % release.version + if not version in self.get_versions(): # append only if not already exists - self.append(release) + self._releases.append(release) for dist in release.dists.values(): for url in dist.urls: self.add_release(version, dist.dist_type, **url) else: - matches = [r for r in self if '%s' % r.version == version - and r.name == self.name] + matches = [r for r in self._releases if '%s' % r.version == version + and r.name == self.name] if not matches: - release = ReleaseInfo(self.name, version) - self.append(release) + release = ReleaseInfo(self.name, version, index=self._index) + self._releases.append(release) else: release = matches[0] @@ -415,21 +442,27 @@ sort_by.append("is_final") sort_by.append("version") - super(ReleasesList, self).sort( + self.releases.sort( key=lambda i: [getattr(i, arg) for arg in sort_by], reverse=reverse, *args, **kwargs) def get_release(self, version): """Return a release from it's version. """ - matches = [r for r in self if "%s" % r.version == version] + matches = [r for r in self.releases if "%s" % r.version == version] if len(matches) != 1: raise KeyError(version) return matches[0] def get_versions(self): """Return a list of releases versions contained""" - return ["%s" % r.version for r in self] + return ["%s" % r.version for r in self._releases] + + def __getitem__(self, key): + return self.releases[key] + + def __len__(self): + return len(self.releases) def __repr__(self): string = 'Project "%s"' % self.name diff --git a/src/distutils2/index/simple.py b/src/distutils2/index/simple.py --- a/src/distutils2/index/simple.py +++ b/src/distutils2/index/simple.py @@ -12,6 +12,7 @@ import urllib2 import urlparse import logging +import os from distutils2.index.base import BaseClient from distutils2.index.dist import (ReleasesList, EXTENSIONS, @@ -20,6 +21,7 @@ UnableToDownload, CantParseArchiveName, ReleaseNotFound, ProjectNotFound) from distutils2.index.mirrors import get_mirrors +from distutils2.metadata import DistributionMetadata from distutils2 import __version__ as __distutils2_version__ __all__ = ['Crawler', 'DEFAULT_SIMPLE_INDEX_URL'] @@ -178,6 +180,25 @@ raise ReleaseNotFound("No release matches the given criterias") return release + def get_distributions(self, project_name, version): + """Return the distributions found on the index for the specific given + release""" + # as the default behavior of get_release is to return a release + # containing the distributions, just alias it. + return self.get_release("%s (%s)" % (project_name, version)) + + def get_metadata(self, project_name, version): + """Return the metadatas from the simple index. + + Currently, download one archive, extract it and use the PKG-INFO file. + """ + release = self.get_distributions(project_name, version) + if not release._metadata: + location = release.get_distribution().unpack() + pkg_info = os.path.join(location, 'PKG-INFO') + release._metadata = DistributionMetadata(pkg_info) + return release + def _switch_to_next_mirror(self): """Switch to the next mirror (eg. point self.index_url to the next mirror url. @@ -238,7 +259,8 @@ else: name = release_info['name'] if not name.lower() in self._projects: - self._projects[name.lower()] = ReleasesList(name) + self._projects[name.lower()] = ReleasesList(name, + index=self._index) if release: self._projects[name.lower()].add_release(release=release) diff --git a/src/distutils2/index/wrapper.py b/src/distutils2/index/wrapper.py --- a/src/distutils2/index/wrapper.py +++ b/src/distutils2/index/wrapper.py @@ -3,7 +3,9 @@ _WRAPPER_MAPPINGS = {'get_release': 'simple', 'get_releases': 'simple', - 'search_projects': 'simple', } + 'search_projects': 'simple', + 'get_metadata': 'xmlrpc', + 'get_distributions': 'simple'} _WRAPPER_INDEXES = {'xmlrpc': xmlrpc.Client, 'simple': simple.Crawler} @@ -58,6 +60,7 @@ for name, cls in index_classes.items(): obj = self._indexes.setdefault(name, cls()) obj._projects = self._projects + obj._index = self def __getattr__(self, method_name): """When asking for methods of the wrapper, return the implementation of diff --git a/src/distutils2/index/xmlrpc.py b/src/distutils2/index/xmlrpc.py --- a/src/distutils2/index/xmlrpc.py +++ b/src/distutils2/index/xmlrpc.py @@ -86,13 +86,14 @@ set(existing_versions)) for version in hidden_versions: project.add_release(release=ReleaseInfo(project_name, - version)) + version, index=self._index)) else: versions = get_versions(project_name, show_hidden) if not versions: raise ProjectNotFound(project_name) project = self._get_project(project_name) - project.add_releases([ReleaseInfo(project_name, version) + project.add_releases([ReleaseInfo(project_name, version, + index=self._index) for version in versions]) project = project.filter(predicate) project.sort_releases(prefer_final) @@ -108,7 +109,8 @@ url_infos = self.proxy.release_urls(project_name, version) project = self._get_project(project_name) if version not in project.get_versions(): - project.add_release(release=ReleaseInfo(project_name, version)) + project.add_release(release=ReleaseInfo(project_name, version, + index=self._index)) release = project.get_release(version) for info in url_infos: packagetype = info['packagetype'] @@ -128,7 +130,8 @@ metadata = self.proxy.release_data(project_name, version) project = self._get_project(project_name) if version not in project.get_versions(): - project.add_release(release=ReleaseInfo(project_name, version)) + project.add_release(release=ReleaseInfo(project_name, version, + index=self._index)) release = project.get_release(version) release.set_metadata(metadata) return release @@ -148,7 +151,8 @@ project = self._get_project(p['name']) try: project.add_release(release=ReleaseInfo(p['name'], - p['version'], metadata={'summary': p['summary']})) + p['version'], metadata={'summary': p['summary']}, + index=self._index)) except IrrationalVersionError, e: logging.warn("Irrational version error found: %s" % e) -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:46 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:46 +0200 Subject: [Python-checkins] distutils2: Update the documentation about indexes Message-ID: tarek.ziade pushed f25f4467e416 to distutils2: http://hg.python.org/distutils2/rev/f25f4467e416 changeset: 470:f25f4467e416 user: Alexis Metaireau date: Thu Jul 29 17:18:02 2010 +0200 summary: Update the documentation about indexes files: docs/source/projects-index.client.rst, docs/source/projects-index.dist.rst, docs/source/projects-index.rst, src/distutils2/index/wrapper.py diff --git a/docs/source/projects-index.client.rst b/docs/source/projects-index.client.rst --- a/docs/source/projects-index.client.rst +++ b/docs/source/projects-index.client.rst @@ -6,22 +6,15 @@ distributions, no matters the underlying API you want to use. The aim of this module is to choose the best way to query the API, using the -less possible XML-RPC, and when possible the simple index. - -.. note:: This index is not yet available, so please rely on XMLRPC client or - Simple Crawler to browse indexes. +less possible XML-RPC, and when possible the simple index. API === -The client comes with the common methods "find", "get" and "download", which -helps to query the servers, and returns. - +The client comes with the common methods "find_projects", "get_release" and +"get_releases", which helps to query the servers, and returns :class:`distutils2.index.dist.ReleaseInfo`, and :class:`distutils2.index.dist.ReleasesList` objects. -XXX TODO Autoclass here. - -Exemples -========= - +.. autoclass:: distutils2.index.wrapper.ClientWrapper + :members: diff --git a/docs/source/projects-index.dist.rst b/docs/source/projects-index.dist.rst --- a/docs/source/projects-index.dist.rst +++ b/docs/source/projects-index.dist.rst @@ -87,9 +87,6 @@ Attributes Lazy loading ----------------------- -.. note:: This is not currently available. So you have to rely on the indexes by - yourself to fill in the fields ! - To abstract a maximum the way of querying informations to the indexes, attributes and releases informations can be retrieved "on demand", in a "lazy" way. @@ -98,8 +95,8 @@ attribute, it can be build directly when accedded:: >>> r = Release("FooBar", "1.1") - >>> r.has_metadata() - False # metadata field is actually set to "None" + >>> print r._metadata + None # metadata field is actually set to "None" >>> r.metadata diff --git a/docs/source/projects-index.rst b/docs/source/projects-index.rst --- a/docs/source/projects-index.rst +++ b/docs/source/projects-index.rst @@ -14,7 +14,7 @@ methods. If you dont care about which API to use, the best thing to do is to let -distutils2 decide this for you, by using :class:`distutils2.index.Client`. +distutils2 decide this for you, by using :class:`distutils2.index.ClientWrapper`. Of course, you can rely too on :class:`distutils2.index.simple.Crawler` and :class:`distutils.index.xmlrpc.Client` if you need to use these specific APIs. diff --git a/src/distutils2/index/wrapper.py b/src/distutils2/index/wrapper.py --- a/src/distutils2/index/wrapper.py +++ b/src/distutils2/index/wrapper.py @@ -39,8 +39,9 @@ class ClientWrapper(object): """Wrapper around simple and xmlrpc clients, - Choose the best implementation to use depending the needs. - If one of the indexes returns an error, try to use others indexes. + Choose the best implementation to use depending the needs, using the given + mappings. + If one of the indexes returns an error, tries to use others indexes. :param index: tell wich index to rely on by default. :param index_classes: a dict of name:class to use as indexes. -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:47 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:47 +0200 Subject: [Python-checkins] distutils2: Update the mirror support accordingly with last MvL changes. Message-ID: tarek.ziade pushed fb8c027af44e to distutils2: http://hg.python.org/distutils2/rev/fb8c027af44e changeset: 473:fb8c027af44e user: Alexis Metaireau date: Fri Jul 30 11:48:45 2010 +0200 summary: Update the mirror support accordingly with last MvL changes. files: src/distutils2/index/mirrors.py diff --git a/src/distutils2/index/mirrors.py b/src/distutils2/index/mirrors.py --- a/src/distutils2/index/mirrors.py +++ b/src/distutils2/index/mirrors.py @@ -22,13 +22,10 @@ # return the last mirror registered on PyPI. try: - unused, aliaslist, ipaddr = socket.gethostbyname_ex(hostname) + hostname = socket.gethostbyname_ex(hostname)[0] except socket.gaierror: return [] - if len(aliaslist) < 2: - return [] - index_adress = aliaslist[-1] - end_letter = index_adress.split(".", 1) + end_letter = hostname.split(".", 1) # determine the list from the last one. return ["%s.%s" % (s, end_letter[1]) for s in string_range(end_letter[0])] -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:47 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:47 +0200 Subject: [Python-checkins] distutils2: Fix the way version predicates are parsed. Message-ID: tarek.ziade pushed 437bda319048 to distutils2: http://hg.python.org/distutils2/rev/437bda319048 changeset: 474:437bda319048 user: Alexis Metaireau date: Mon Aug 02 12:40:31 2010 +0200 summary: Fix the way version predicates are parsed. files: src/distutils2/tests/test_version.py, src/distutils2/version.py diff --git a/src/distutils2/tests/test_version.py b/src/distutils2/tests/test_version.py --- a/src/distutils2/tests/test_version.py +++ b/src/distutils2/tests/test_version.py @@ -188,6 +188,13 @@ # XXX need to silent the micro version in this case #assert not VersionPredicate('Ho (<3.0,!=2.6)').match('2.6.3') + def test_predicate_name(self): + # Test that names are parsed the right way + + self.assertEqual('Hey', VersionPredicate('Hey (<1.1)').name) + self.assertEqual('Foo-Bar', VersionPredicate('Foo-Bar (1.1)').name) + self.assertEqual('Foo Bar', VersionPredicate('Foo Bar (1.1)').name) + def test_is_final(self): # VersionPredicate knows is a distribution is a final one or not. final_versions = ('1.0', '1.0.post456') diff --git a/src/distutils2/version.py b/src/distutils2/version.py --- a/src/distutils2/version.py +++ b/src/distutils2/version.py @@ -321,7 +321,7 @@ return None -_PREDICATE = re.compile(r"(?i)^\s*([a-z_]\w*(?:\.[a-z_]\w*)*)(.*)") +_PREDICATE = re.compile(r"(?i)^\s*([a-z_][\sa-zA-Z_-]*(?:\.[a-z_]\w*)*)(.*)") _VERSIONS = re.compile(r"^\s*\((.*)\)\s*$") _PLAIN_VERSIONS = re.compile(r"^\s*(.*)\s*$") _SPLIT_CMP = re.compile(r"^\s*(<=|>=|<|>|!=|==)\s*([^\s,]+)\s*$") @@ -354,7 +354,8 @@ if match is None: raise ValueError('Bad predicate "%s"' % predicate) - self.name, predicates = match.groups() + name, predicates = match.groups() + self.name = name.strip() predicates = predicates.strip() predicates = _VERSIONS.match(predicates) if predicates is not None: -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:47 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:47 +0200 Subject: [Python-checkins] distutils2: Merge upstream. Fixing conflicts in util.py Message-ID: tarek.ziade pushed 2042fc40d324 to distutils2: http://hg.python.org/distutils2/rev/2042fc40d324 changeset: 472:2042fc40d324 parent: 471:06cb3de6c55a parent: 387:ba37b166e4f9 user: Alexis Metaireau date: Thu Jul 29 19:33:05 2010 +0200 summary: Merge upstream. Fixing conflicts in util.py files: src/distutils2/config.py, src/distutils2/spawn.py, src/distutils2/tests/test_config.py, src/distutils2/tests/test_spawn.py, src/distutils2/util.py diff --git a/src/check.sh b/src/check.sh new file mode 100755 --- /dev/null +++ b/src/check.sh @@ -0,0 +1,2 @@ +pep8 distutils2 +pyflakes distutils2 diff --git a/src/distutils2/command/cmd.py b/src/distutils2/command/cmd.py --- a/src/distutils2/command/cmd.py +++ b/src/distutils2/command/cmd.py @@ -411,7 +411,7 @@ def spawn(self, cmd, search_path=1, level=1): """Spawn an external command respecting dry-run flag.""" - from distutils2.spawn import spawn + from distutils2.util import spawn spawn(cmd, search_path, dry_run= self.dry_run) def make_archive(self, base_name, format, root_dir=None, base_dir=None, diff --git a/src/distutils2/command/register.py b/src/distutils2/command/register.py --- a/src/distutils2/command/register.py +++ b/src/distutils2/command/register.py @@ -13,28 +13,40 @@ import StringIO from warnings import warn -from distutils2.core import PyPIRCCommand +from distutils2.command.cmd import Command from distutils2 import log +from distutils2.util import (metadata_to_dict, read_pypirc, generate_pypirc, + DEFAULT_REPOSITORY, DEFAULT_REALM, + get_pypirc_path) -class register(PyPIRCCommand): +class register(Command): description = ("register the distribution with the Python package index") - user_options = PyPIRCCommand.user_options + [ + user_options = [('repository=', 'r', + "url of repository [default: %s]" % DEFAULT_REPOSITORY), + ('show-response', None, + 'display full response text from server'), ('list-classifiers', None, 'list the valid Trove classifiers'), ('strict', None , 'Will stop the registering if the meta-data are not fully compliant') ] - boolean_options = PyPIRCCommand.boolean_options + [ - 'verify', 'list-classifiers', 'strict'] + + boolean_options = ['show-response', 'verify', 'list-classifiers', + 'strict'] def initialize_options(self): - PyPIRCCommand.initialize_options(self) + self.repository = None + self.realm = None + self.show_response = 0 self.list_classifiers = 0 self.strict = 0 def finalize_options(self): - PyPIRCCommand.finalize_options(self) + if self.repository is None: + self.repository = DEFAULT_REPOSITORY + if self.realm is None: + self.realm = DEFAULT_REALM # setting options for the `check` subcommand check_options = {'strict': ('register', self.strict), 'restructuredtext': ('register', 1)} @@ -67,7 +79,7 @@ def _set_config(self): ''' Reads the configuration file and set attributes. ''' - config = self._read_pypirc() + config = read_pypirc(self.repository, self.realm) if config != {}: self.username = config['username'] self.password = config['password'] @@ -75,10 +87,10 @@ self.realm = config['realm'] self.has_config = True else: - if self.repository not in ('pypi', self.DEFAULT_REPOSITORY): + if self.repository not in ('pypi', DEFAULT_REPOSITORY): raise ValueError('%s not found in .pypirc' % self.repository) if self.repository == 'pypi': - self.repository = self.DEFAULT_REPOSITORY + self.repository = DEFAULT_REPOSITORY self.has_config = False def classifiers(self): @@ -177,14 +189,14 @@ self.announce(('I can store your PyPI login so future ' 'submissions will be faster.'), log.INFO) self.announce('(the login will be stored in %s)' % \ - self._get_rc_file(), log.INFO) + get_pypirc_path(), log.INFO) choice = 'X' while choice.lower() not in 'yn': choice = raw_input('Save your login (y/N)?') if not choice: choice = 'n' if choice.lower() == 'y': - self._store_pypirc(username, password) + generate_pypirc(username, password) elif choice == '2': data = {':action': 'user'} @@ -221,7 +233,7 @@ def build_post_data(self, action): # figure the data to send - the metadata plus some additional # information used by the package server - data = self._metadata_to_pypy_dict() + data = metadata_to_dict(self.distribution.metadata) data[':action'] = action return data diff --git a/src/distutils2/command/upload.py b/src/distutils2/command/upload.py --- a/src/distutils2/command/upload.py +++ b/src/distutils2/command/upload.py @@ -14,24 +14,34 @@ from md5 import md5 from distutils2.errors import DistutilsOptionError -from distutils2.core import PyPIRCCommand -from distutils2.spawn import spawn +from distutils2.util import spawn from distutils2 import log +from distutils2.command.cmd import Command +from distutils2 import log +from distutils2.util import (metadata_to_dict, read_pypirc, + DEFAULT_REPOSITORY, DEFAULT_REALM) -class upload(PyPIRCCommand): + +class upload(Command): description = "upload binary package to PyPI" - user_options = PyPIRCCommand.user_options + [ + user_options = [('repository=', 'r', + "url of repository [default: %s]" % \ + DEFAULT_REPOSITORY), + ('show-response', None, + 'display full response text from server'), ('sign', 's', 'sign files to upload using gpg'), ('identity=', 'i', 'GPG identity used to sign files'), ] - boolean_options = PyPIRCCommand.boolean_options + ['sign'] + boolean_options = ['show-response', 'sign'] def initialize_options(self): - PyPIRCCommand.initialize_options(self) + self.repository = None + self.realm = None + self.show_response = 0 self.username = '' self.password = '' self.show_response = 0 @@ -39,12 +49,15 @@ self.identity = None def finalize_options(self): - PyPIRCCommand.finalize_options(self) + if self.repository is None: + self.repository = DEFAULT_REPOSITORY + if self.realm is None: + self.realm = DEFAULT_REALM if self.identity and not self.sign: raise DistutilsOptionError( "Must use --sign for --identity to have meaning" ) - config = self._read_pypirc() + config = read_pypirc(self.repository, self.realm) if config != {}: self.username = config['username'] self.password = config['password'] @@ -85,7 +98,7 @@ # register a new release content = open(filename,'rb').read() - data = self._metadata_to_pypy_dict() + data = metadata_to_dict(self.distribution.metadata) # extra upload infos data[':action'] = 'file_upload' diff --git a/src/distutils2/command/upload_docs.py b/src/distutils2/command/upload_docs.py --- a/src/distutils2/command/upload_docs.py +++ b/src/distutils2/command/upload_docs.py @@ -1,9 +1,11 @@ import base64, httplib, os.path, socket, urlparse, zipfile from cStringIO import StringIO + from distutils2 import log from distutils2.command.upload import upload -from distutils2.core import PyPIRCCommand +from distutils2.command.cmd import Command from distutils2.errors import DistutilsFileError +from distutils2.util import read_pypirc, DEFAULT_REPOSITORY, DEFAULT_REALM def zip_dir(directory): """Compresses recursively contents of directory into a StringIO object""" @@ -47,26 +49,33 @@ content_type = 'multipart/form-data; boundary=%s' % boundary return content_type, body -class upload_docs(PyPIRCCommand): +class upload_docs(Command): user_options = [ - ('repository=', 'r', "url of repository [default: %s]" % upload.DEFAULT_REPOSITORY), + ('repository=', 'r', "url of repository [default: %s]" % DEFAULT_REPOSITORY), ('show-response', None, 'display full response text from server'), ('upload-dir=', None, 'directory to upload'), ] def initialize_options(self): - PyPIRCCommand.initialize_options(self) + self.repository = None + self.realm = None + self.show_response = 0 self.upload_dir = "build/docs" + self.username = '' + self.password = '' def finalize_options(self): - PyPIRCCommand.finalize_options(self) + if self.repository is None: + self.repository = DEFAULT_REPOSITORY + if self.realm is None: + self.realm = DEFAULT_REALM if self.upload_dir == None: build = self.get_finalized_command('build') self.upload_dir = os.path.join(build.build_base, "docs") self.announce('Using upload directory %s' % self.upload_dir) self.verify_upload_dir(self.upload_dir) - config = self._read_pypirc() + config = read_pypirc(self.repository, self.realm) if config != {}: self.username = config['username'] self.password = config['password'] diff --git a/src/distutils2/compiler/ccompiler.py b/src/distutils2/compiler/ccompiler.py --- a/src/distutils2/compiler/ccompiler.py +++ b/src/distutils2/compiler/ccompiler.py @@ -11,8 +11,7 @@ from distutils2.errors import (CompileError, LinkError, UnknownFileError, DistutilsPlatformError, DistutilsModuleError) -from distutils2.spawn import spawn -from distutils2.util import split_quoted, execute, newer_group +from distutils2.util import split_quoted, execute, newer_group, spawn from distutils2 import log from shutil import move diff --git a/src/distutils2/config.py b/src/distutils2/config.py deleted file mode 100644 --- a/src/distutils2/config.py +++ /dev/null @@ -1,155 +0,0 @@ -"""distutils.pypirc - -Provides the PyPIRCCommand class, the base class for the command classes -that uses .pypirc in the distutils.command package. -""" -import os -from ConfigParser import RawConfigParser - -from distutils2.command.cmd import Command - -DEFAULT_PYPIRC = """\ -[distutils] -index-servers = - pypi - -[pypi] -username:%s -password:%s -""" - -class PyPIRCCommand(Command): - """Base command that knows how to handle the .pypirc file - """ - DEFAULT_REPOSITORY = 'http://pypi.python.org/pypi' - DEFAULT_REALM = 'pypi' - repository = None - realm = None - - user_options = [ - ('repository=', 'r', - "url of repository [default: %s]" % \ - DEFAULT_REPOSITORY), - ('show-response', None, - 'display full response text from server')] - - boolean_options = ['show-response'] - - def _get_rc_file(self): - """Returns rc file path.""" - return os.path.join(os.path.expanduser('~'), '.pypirc') - - def _store_pypirc(self, username, password): - """Creates a default .pypirc file.""" - rc = self._get_rc_file() - f = open(rc, 'w') - try: - f.write(DEFAULT_PYPIRC % (username, password)) - finally: - f.close() - try: - os.chmod(rc, 0600) - except OSError: - # should do something better here - pass - - def _read_pypirc(self): - """Reads the .pypirc file.""" - rc = self._get_rc_file() - if os.path.exists(rc): - self.announce('Using PyPI login from %s' % rc) - repository = self.repository or self.DEFAULT_REPOSITORY - config = RawConfigParser() - config.read(rc) - sections = config.sections() - if 'distutils' in sections: - # let's get the list of servers - index_servers = config.get('distutils', 'index-servers') - _servers = [server.strip() for server in - index_servers.split('\n') - if server.strip() != ''] - if _servers == []: - # nothing set, let's try to get the default pypi - if 'pypi' in sections: - _servers = ['pypi'] - else: - # the file is not properly defined, returning - # an empty dict - return {} - for server in _servers: - current = {'server': server} - current['username'] = config.get(server, 'username') - - # optional params - for key, default in (('repository', - self.DEFAULT_REPOSITORY), - ('realm', self.DEFAULT_REALM), - ('password', None)): - if config.has_option(server, key): - current[key] = config.get(server, key) - else: - current[key] = default - if (current['server'] == repository or - current['repository'] == repository): - return current - elif 'server-login' in sections: - # old format - server = 'server-login' - if config.has_option(server, 'repository'): - repository = config.get(server, 'repository') - else: - repository = self.DEFAULT_REPOSITORY - return {'username': config.get(server, 'username'), - 'password': config.get(server, 'password'), - 'repository': repository, - 'server': server, - 'realm': self.DEFAULT_REALM} - - return {} - - def _metadata_to_pypy_dict(self): - meta = self.distribution.metadata - data = { - 'metadata_version' : meta.version, - 'name': meta['Name'], - 'version': meta['Version'], - 'summary': meta['Summary'], - 'home_page': meta['Home-page'], - 'author': meta['Author'], - 'author_email': meta['Author-email'], - 'license': meta['License'], - 'description': meta['Description'], - 'keywords': meta['Keywords'], - 'platform': meta['Platform'], - 'classifier': meta['Classifier'], - 'download_url': meta['Download-URL'], - } - - if meta.version == '1.2': - data['requires_dist'] = meta['Requires-Dist'] - data['requires_python'] = meta['Requires-Python'] - data['requires_external'] = meta['Requires-External'] - data['provides_dist'] = meta['Provides-Dist'] - data['obsoletes_dist'] = meta['Obsoletes-Dist'] - data['project_url'] = [','.join(url) for url in - meta['Project-URL']] - - elif meta.version == '1.1': - data['provides'] = meta['Provides'] - data['requires'] = meta['Requires'] - data['obsoletes'] = meta['Obsoletes'] - - return data - - def initialize_options(self): - """Initialize options.""" - self.repository = None - self.realm = None - self.show_response = 0 - - def finalize_options(self): - """Finalizes options.""" - if self.repository is None: - self.repository = self.DEFAULT_REPOSITORY - if self.realm is None: - self.realm = self.DEFAULT_REALM diff --git a/src/distutils2/core.py b/src/distutils2/core.py --- a/src/distutils2/core.py +++ b/src/distutils2/core.py @@ -19,7 +19,6 @@ # Mainly import these so setup scripts can "from distutils2.core import" them. from distutils2.dist import Distribution from distutils2.command.cmd import Command -from distutils2.config import PyPIRCCommand from distutils2.extension import Extension from distutils2.util import find_packages diff --git a/src/distutils2/spawn.py b/src/distutils2/spawn.py deleted file mode 100644 --- a/src/distutils2/spawn.py +++ /dev/null @@ -1,192 +0,0 @@ -"""distutils.spawn - -Provides the 'spawn()' function, a front-end to various platform- -specific functions for launching another program in a sub-process. -Also provides the 'find_executable()' to search the path for a given -executable name. -""" - -__revision__ = "$Id: spawn.py 73147 2009-06-02 15:58:43Z tarek.ziade $" - -import sys -import os - -from distutils2.errors import DistutilsPlatformError, DistutilsExecError -from distutils2 import log - -def spawn(cmd, search_path=1, verbose=0, dry_run=0, env=None): - """Run another program, specified as a command list 'cmd', in a new process. - - 'cmd' is just the argument list for the new process, ie. - cmd[0] is the program to run and cmd[1:] are the rest of its arguments. - There is no way to run a program with a name different from that of its - executable. - - If 'search_path' is true (the default), the system's executable - search path will be used to find the program; otherwise, cmd[0] - must be the exact path to the executable. If 'dry_run' is true, - the command will not actually be run. - - If 'env' is given, it's a environment dictionary used for the execution - environment. - - Raise DistutilsExecError if running the program fails in any way; just - return on success. - """ - if os.name == 'posix': - _spawn_posix(cmd, search_path, dry_run=dry_run, env=env) - elif os.name == 'nt': - _spawn_nt(cmd, search_path, dry_run=dry_run, env=env) - elif os.name == 'os2': - _spawn_os2(cmd, search_path, dry_run=dry_run, env=env) - else: - raise DistutilsPlatformError, \ - "don't know how to spawn programs on platform '%s'" % os.name - -def _nt_quote_args(args): - """Quote command-line arguments for DOS/Windows conventions. - - Just wraps every argument which contains blanks in double quotes, and - returns a new argument list. - """ - # XXX this doesn't seem very robust to me -- but if the Windows guys - # say it'll work, I guess I'll have to accept it. (What if an arg - # contains quotes? What other magic characters, other than spaces, - # have to be escaped? Is there an escaping mechanism other than - # quoting?) - for i, arg in enumerate(args): - if ' ' in arg: - args[i] = '"%s"' % arg - return args - -def _spawn_nt(cmd, search_path=1, verbose=0, dry_run=0, env=None): - executable = cmd[0] - cmd = _nt_quote_args(cmd) - if search_path: - # either we find one or it stays the same - executable = find_executable(executable) or executable - log.info(' '.join([executable] + cmd[1:])) - if not dry_run: - # spawn for NT requires a full path to the .exe - try: - if env is None: - rc = os.spawnv(os.P_WAIT, executable, cmd) - else: - rc = os.spawnve(os.P_WAIT, executable, cmd, env) - - except OSError, exc: - # this seems to happen when the command isn't found - raise DistutilsExecError, \ - "command '%s' failed: %s" % (cmd[0], exc[-1]) - if rc != 0: - # and this reflects the command running but failing - raise DistutilsExecError, \ - "command '%s' failed with exit status %d" % (cmd[0], rc) - -def _spawn_os2(cmd, search_path=1, verbose=0, dry_run=0, env=None): - executable = cmd[0] - if search_path: - # either we find one or it stays the same - executable = find_executable(executable) or executable - log.info(' '.join([executable] + cmd[1:])) - if not dry_run: - # spawnv for OS/2 EMX requires a full path to the .exe - try: - if env is None: - rc = os.spawnv(os.P_WAIT, executable, cmd) - else: - rc = os.spawnve(os.P_WAIT, executable, cmd, env) - - except OSError, exc: - # this seems to happen when the command isn't found - raise DistutilsExecError, \ - "command '%s' failed: %s" % (cmd[0], exc[-1]) - if rc != 0: - # and this reflects the command running but failing - log.debug("command '%s' failed with exit status %d" % (cmd[0], rc)) - raise DistutilsExecError, \ - "command '%s' failed with exit status %d" % (cmd[0], rc) - - -def _spawn_posix(cmd, search_path=1, verbose=0, dry_run=0, env=None): - log.info(' '.join(cmd)) - if dry_run: - return - - if env is None: - exec_fn = search_path and os.execvp or os.execv - else: - exec_fn = search_path and os.execvpe or os.execve - - pid = os.fork() - - if pid == 0: # in the child - try: - if env is None: - exec_fn(cmd[0], cmd) - else: - exec_fn(cmd[0], cmd, env) - except OSError, e: - sys.stderr.write("unable to execute %s: %s\n" % - (cmd[0], e.strerror)) - os._exit(1) - - sys.stderr.write("unable to execute %s for unknown reasons" % cmd[0]) - os._exit(1) - else: # in the parent - # Loop until the child either exits or is terminated by a signal - # (ie. keep waiting if it's merely stopped) - while 1: - try: - pid, status = os.waitpid(pid, 0) - except OSError, exc: - import errno - if exc.errno == errno.EINTR: - continue - raise DistutilsExecError, \ - "command '%s' failed: %s" % (cmd[0], exc[-1]) - if os.WIFSIGNALED(status): - raise DistutilsExecError, \ - "command '%s' terminated by signal %d" % \ - (cmd[0], os.WTERMSIG(status)) - - elif os.WIFEXITED(status): - exit_status = os.WEXITSTATUS(status) - if exit_status == 0: - return # hey, it succeeded! - else: - raise DistutilsExecError, \ - "command '%s' failed with exit status %d" % \ - (cmd[0], exit_status) - - elif os.WIFSTOPPED(status): - continue - - else: - raise DistutilsExecError, \ - "unknown error executing '%s': termination status %d" % \ - (cmd[0], status) - -def find_executable(executable, path=None): - """Tries to find 'executable' in the directories listed in 'path'. - - A string listing directories separated by 'os.pathsep'; defaults to - os.environ['PATH']. Returns the complete filename or None if not found. - """ - if path is None: - path = os.environ['PATH'] - paths = path.split(os.pathsep) - base, ext = os.path.splitext(executable) - - if (sys.platform == 'win32' or os.name == 'os2') and (ext != '.exe'): - executable = executable + '.exe' - - if not os.path.isfile(executable): - for p in paths: - f = os.path.join(p, executable) - if os.path.isfile(f): - # the file exists, we have a shot at spawn working - return f - return None - else: - return executable diff --git a/src/distutils2/tests/test_bdist.py b/src/distutils2/tests/test_bdist.py --- a/src/distutils2/tests/test_bdist.py +++ b/src/distutils2/tests/test_bdist.py @@ -8,8 +8,7 @@ from distutils2.command.bdist import bdist from distutils2.tests import support from distutils2.tests.support import unittest -from distutils2.spawn import find_executable -from distutils2 import spawn +from distutils2.util import find_executable from distutils2.errors import DistutilsExecError class BuildTestCase(support.TempdirManager, diff --git a/src/distutils2/tests/test_build_clib.py b/src/distutils2/tests/test_build_clib.py --- a/src/distutils2/tests/test_build_clib.py +++ b/src/distutils2/tests/test_build_clib.py @@ -5,7 +5,7 @@ from distutils2.command.build_clib import build_clib from distutils2.errors import DistutilsSetupError from distutils2.tests import support -from distutils2.spawn import find_executable +from distutils2.util import find_executable from distutils2.tests.support import unittest class BuildCLibTestCase(support.TempdirManager, diff --git a/src/distutils2/tests/test_config.py b/src/distutils2/tests/test_config.py deleted file mode 100644 --- a/src/distutils2/tests/test_config.py +++ /dev/null @@ -1,117 +0,0 @@ -"""Tests for distutils.pypirc.pypirc.""" -import sys -import os -import shutil - -from distutils2.core import PyPIRCCommand -from distutils2.core import Distribution -from distutils2.log import set_threshold -from distutils2.log import WARN - -from distutils2.tests import support -from distutils2.tests.support import unittest - -PYPIRC = """\ -[distutils] - -index-servers = - server1 - server2 - -[server1] -username:me -password:secret - -[server2] -username:meagain -password: secret -realm:acme -repository:http://another.pypi/ -""" - -PYPIRC_OLD = """\ -[server-login] -username:tarek -password:secret -""" - -WANTED = """\ -[distutils] -index-servers = - pypi - -[pypi] -username:tarek -password:xxx -""" - - -class PyPIRCCommandTestCase(support.TempdirManager, - support.LoggingSilencer, - support.EnvironGuard, - unittest.TestCase): - - def setUp(self): - """Patches the environment.""" - super(PyPIRCCommandTestCase, self).setUp() - self.tmp_dir = self.mkdtemp() - os.environ['HOME'] = self.tmp_dir - self.rc = os.path.join(self.tmp_dir, '.pypirc') - self.dist = Distribution() - - class command(PyPIRCCommand): - def __init__(self, dist): - PyPIRCCommand.__init__(self, dist) - def initialize_options(self): - pass - finalize_options = initialize_options - - self._cmd = command - self.old_threshold = set_threshold(WARN) - - def tearDown(self): - """Removes the patch.""" - set_threshold(self.old_threshold) - super(PyPIRCCommandTestCase, self).tearDown() - - def test_server_registration(self): - # This test makes sure PyPIRCCommand knows how to: - # 1. handle several sections in .pypirc - # 2. handle the old format - - # new format - self.write_file(self.rc, PYPIRC) - cmd = self._cmd(self.dist) - config = cmd._read_pypirc() - - config = config.items() - config.sort() - expected = [('password', 'secret'), ('realm', 'pypi'), - ('repository', 'http://pypi.python.org/pypi'), - ('server', 'server1'), ('username', 'me')] - self.assertEqual(config, expected) - - # old format - self.write_file(self.rc, PYPIRC_OLD) - config = cmd._read_pypirc() - config = config.items() - config.sort() - expected = [('password', 'secret'), ('realm', 'pypi'), - ('repository', 'http://pypi.python.org/pypi'), - ('server', 'server-login'), ('username', 'tarek')] - self.assertEqual(config, expected) - - def test_server_empty_registration(self): - cmd = self._cmd(self.dist) - rc = cmd._get_rc_file() - self.assertTrue(not os.path.exists(rc)) - cmd._store_pypirc('tarek', 'xxx') - self.assertTrue(os.path.exists(rc)) - content = open(rc).read() - self.assertEqual(content, WANTED) - -def test_suite(): - return unittest.makeSuite(PyPIRCCommandTestCase) - -if __name__ == "__main__": - unittest.main(defaultTest="test_suite") diff --git a/src/distutils2/tests/test_register.py b/src/distutils2/tests/test_register.py --- a/src/distutils2/tests/test_register.py +++ b/src/distutils2/tests/test_register.py @@ -19,7 +19,7 @@ from distutils2.tests import support from distutils2.tests.support import unittest -from distutils2.tests.test_config import PYPIRC, PyPIRCCommandTestCase + PYPIRC_NOPASSWORD = """\ [distutils] @@ -68,10 +68,15 @@ def read(self): return 'xxx' -class RegisterTestCase(PyPIRCCommandTestCase): +class RegisterTestCase(support.TempdirManager, support.EnvironGuard, + unittest.TestCase): def setUp(self): super(RegisterTestCase, self).setUp() + self.tmp_dir = self.mkdtemp() + self.rc = os.path.join(self.tmp_dir, '.pypirc') + os.environ['HOME'] = self.tmp_dir + # patching the password prompt self._old_getpass = getpass.getpass def _getpass(prompt): @@ -148,8 +153,8 @@ self.write_file(self.rc, PYPIRC_NOPASSWORD) cmd = self._get_cmd() + cmd.finalize_options() cmd._set_config() - cmd.finalize_options() cmd.send_metadata() # dist.password should be set diff --git a/src/distutils2/tests/test_sdist.py b/src/distutils2/tests/test_sdist.py --- a/src/distutils2/tests/test_sdist.py +++ b/src/distutils2/tests/test_sdist.py @@ -28,9 +28,8 @@ from distutils2.command.sdist import show_formats from distutils2.core import Distribution from distutils2.tests.support import unittest -from distutils2.tests.test_config import PyPIRCCommandTestCase from distutils2.errors import DistutilsExecError, DistutilsOptionError -from distutils2.spawn import find_executable +from distutils2.util import find_executable from distutils2.tests import support from distutils2.log import WARN try: @@ -58,12 +57,15 @@ somecode%(sep)sdoc.txt """ -class SDistTestCase(PyPIRCCommandTestCase): +class SDistTestCase(support.TempdirManager, support.LoggingSilencer, + support.EnvironGuard, unittest.TestCase): def setUp(self): # PyPIRCCommandTestCase creates a temp dir already # and put it in self.tmp_dir super(SDistTestCase, self).setUp() + self.tmp_dir = self.mkdtemp() + os.environ['HOME'] = self.tmp_dir # setting up an environment self.old_path = os.getcwd() os.mkdir(join(self.tmp_dir, 'somecode')) diff --git a/src/distutils2/tests/test_spawn.py b/src/distutils2/tests/test_spawn.py deleted file mode 100644 --- a/src/distutils2/tests/test_spawn.py +++ /dev/null @@ -1,60 +0,0 @@ -"""Tests for distutils.spawn.""" -import os -import time -from distutils2.tests import captured_stdout - -from distutils2.spawn import _nt_quote_args -from distutils2.spawn import spawn, find_executable -from distutils2.errors import DistutilsExecError -from distutils2.tests import support -from distutils2.tests.support import unittest - -class SpawnTestCase(support.TempdirManager, - support.LoggingSilencer, - unittest.TestCase): - - def test_nt_quote_args(self): - - for (args, wanted) in ((['with space', 'nospace'], - ['"with space"', 'nospace']), - (['nochange', 'nospace'], - ['nochange', 'nospace'])): - res = _nt_quote_args(args) - self.assertEqual(res, wanted) - - - @unittest.skipUnless(os.name in ('nt', 'posix'), - 'Runs only under posix or nt') - def test_spawn(self): - tmpdir = self.mkdtemp() - - # creating something executable - # through the shell that returns 1 - if os.name == 'posix': - exe = os.path.join(tmpdir, 'foo.sh') - self.write_file(exe, '#!/bin/sh\nexit 1') - os.chmod(exe, 0777) - else: - exe = os.path.join(tmpdir, 'foo.bat') - self.write_file(exe, 'exit 1') - - os.chmod(exe, 0777) - self.assertRaises(DistutilsExecError, spawn, [exe]) - - # now something that works - if os.name == 'posix': - exe = os.path.join(tmpdir, 'foo.sh') - self.write_file(exe, '#!/bin/sh\nexit 0') - os.chmod(exe, 0777) - else: - exe = os.path.join(tmpdir, 'foo.bat') - self.write_file(exe, 'exit 0') - - os.chmod(exe, 0777) - spawn([exe]) # should work without any error - -def test_suite(): - return unittest.makeSuite(SpawnTestCase) - -if __name__ == "__main__": - unittest.main(defaultTest="test_suite") diff --git a/src/distutils2/tests/test_upload.py b/src/distutils2/tests/test_upload.py --- a/src/distutils2/tests/test_upload.py +++ b/src/distutils2/tests/test_upload.py @@ -9,7 +9,6 @@ from distutils2.tests import support from distutils2.tests.pypi_server import PyPIServer, PyPIServerTestCase from distutils2.tests.support import unittest -from distutils2.tests.test_config import PYPIRC, PyPIRCCommandTestCase PYPIRC_NOPASSWORD = """\ @@ -22,7 +21,33 @@ username:me """ -class UploadTestCase(PyPIServerTestCase, PyPIRCCommandTestCase): +PYPIRC = """\ +[distutils] + +index-servers = + server1 + server2 + +[server1] +username:me +password:secret + +[server2] +username:meagain +password: secret +realm:acme +repository:http://another.pypi/ +""" + + +class UploadTestCase(support.TempdirManager, support.EnvironGuard, + PyPIServerTestCase): + + def setUp(self): + super(UploadTestCase, self).setUp() + self.tmp_dir = self.mkdtemp() + self.rc = os.path.join(self.tmp_dir, '.pypirc') + os.environ['HOME'] = self.tmp_dir def test_finalize_options(self): # new format diff --git a/src/distutils2/tests/test_upload_docs.py b/src/distutils2/tests/test_upload_docs.py --- a/src/distutils2/tests/test_upload_docs.py +++ b/src/distutils2/tests/test_upload_docs.py @@ -12,7 +12,6 @@ from distutils2.tests import support from distutils2.tests.pypi_server import PyPIServer, PyPIServerTestCase -from distutils2.tests.test_config import PyPIRCCommandTestCase from distutils2.tests.support import unittest @@ -47,10 +46,14 @@ password = long_island """ -class UploadDocsTestCase(PyPIServerTestCase, PyPIRCCommandTestCase): +class UploadDocsTestCase(support.TempdirManager, support.EnvironGuard, + PyPIServerTestCase): def setUp(self): super(UploadDocsTestCase, self).setUp() + self.tmp_dir = self.mkdtemp() + self.rc = os.path.join(self.tmp_dir, '.pypirc') + os.environ['HOME'] = self.tmp_dir self.dist = Distribution() self.dist.metadata['Name'] = "distr-name" self.cmd = upload_docs(self.dist) @@ -178,9 +181,13 @@ def test_show_response(self): orig_stdout = sys.stdout write_args = [] + class MockStdIn(object): def write(self, arg): write_args.append(arg) + def flush(self): + pass + sys.stdout = MockStdIn() try: self.prepare_command() @@ -188,8 +195,10 @@ self.cmd.run() finally: sys.stdout = orig_stdout + self.assertTrue(write_args[0], "should report the response") - self.assertIn(self.pypi.default_response_data + "\n", write_args[0]) + self.assertIn(self.pypi.default_response_data + "\n", + '\n'.join(write_args)) def test_suite(): return unittest.makeSuite(UploadDocsTestCase) diff --git a/src/distutils2/tests/test_util.py b/src/distutils2/tests/test_util.py --- a/src/distutils2/tests/test_util.py +++ b/src/distutils2/tests/test_util.py @@ -7,19 +7,58 @@ import tempfile import time +from distutils2.tests import captured_stdout +from distutils2.tests.support import unittest from distutils2.errors import (DistutilsPlatformError, DistutilsByteCompileError, - DistutilsFileError) + DistutilsFileError, + DistutilsExecError) +from distutils2.util import (convert_path, change_root, + check_environ, split_quoted, strtobool, + rfc822_escape, get_compiler_versions, + _find_exe_version, _MAC_OS_X_LD_VERSION, + byte_compile, find_packages, spawn, find_executable, + _nt_quote_args, get_pypirc_path, generate_pypirc, + read_pypirc) -from distutils2.util import (convert_path, change_root, - check_environ, split_quoted, strtobool, - rfc822_escape, get_compiler_versions, - _find_exe_version, _MAC_OS_X_LD_VERSION, - byte_compile, find_packages) from distutils2 import util from distutils2.tests import support from distutils2.tests.support import unittest + +PYPIRC = """\ +[distutils] +index-servers = + pypi + server1 + +[pypi] +username:me +password:xxxx + +[server1] +repository:http://example.com +username:tarek +password:secret +""" + +PYPIRC_OLD = """\ +[server-login] +username:tarek +password:secret +""" + +WANTED = """\ +[distutils] +index-servers = + pypi + +[pypi] +username:tarek +password:xxx +""" + + class FakePopen(object): test_class = None def __init__(self, cmd, shell, stdout, stderr): @@ -40,6 +79,9 @@ def setUp(self): super(UtilTestCase, self).setUp() + self.tmp_dir = self.mkdtemp() + self.rc = os.path.join(self.tmp_dir, '.pypirc') + os.environ['HOME'] = self.tmp_dir # saving the environment self.name = os.name self.platform = sys.platform @@ -332,6 +374,80 @@ file_handle.close() self.assertEquals(new_content, converted_content) + def test_nt_quote_args(self): + + for (args, wanted) in ((['with space', 'nospace'], + ['"with space"', 'nospace']), + (['nochange', 'nospace'], + ['nochange', 'nospace'])): + res = _nt_quote_args(args) + self.assertEqual(res, wanted) + + + @unittest.skipUnless(os.name in ('nt', 'posix'), + 'Runs only under posix or nt') + def test_spawn(self): + tmpdir = self.mkdtemp() + + # creating something executable + # through the shell that returns 1 + if os.name == 'posix': + exe = os.path.join(tmpdir, 'foo.sh') + self.write_file(exe, '#!/bin/sh\nexit 1') + os.chmod(exe, 0777) + else: + exe = os.path.join(tmpdir, 'foo.bat') + self.write_file(exe, 'exit 1') + + os.chmod(exe, 0777) + self.assertRaises(DistutilsExecError, spawn, [exe]) + + # now something that works + if os.name == 'posix': + exe = os.path.join(tmpdir, 'foo.sh') + self.write_file(exe, '#!/bin/sh\nexit 0') + os.chmod(exe, 0777) + else: + exe = os.path.join(tmpdir, 'foo.bat') + self.write_file(exe, 'exit 0') + + os.chmod(exe, 0777) + spawn([exe]) # should work without any error + + def test_server_registration(self): + # This test makes sure we know how to: + # 1. handle several sections in .pypirc + # 2. handle the old format + + # new format + self.write_file(self.rc, PYPIRC) + config = read_pypirc() + + config = config.items() + config.sort() + expected = [('password', 'xxxx'), ('realm', 'pypi'), + ('repository', 'http://pypi.python.org/pypi'), + ('server', 'pypi'), ('username', 'me')] + self.assertEqual(config, expected) + + # old format + self.write_file(self.rc, PYPIRC_OLD) + config = read_pypirc() + config = config.items() + config.sort() + expected = [('password', 'secret'), ('realm', 'pypi'), + ('repository', 'http://pypi.python.org/pypi'), + ('server', 'server-login'), ('username', 'tarek')] + self.assertEqual(config, expected) + + def test_server_empty_registration(self): + rc = get_pypirc_path() + self.assertTrue(not os.path.exists(rc)) + generate_pypirc('tarek', 'xxx') + self.assertTrue(os.path.exists(rc)) + content = open(rc).read() + self.assertEqual(content, WANTED) + def test_suite(): return unittest.makeSuite(UtilTestCase) diff --git a/src/distutils2/util.py b/src/distutils2/util.py --- a/src/distutils2/util.py +++ b/src/distutils2/util.py @@ -15,10 +15,10 @@ import zipfile from copy import copy from fnmatch import fnmatchcase +from ConfigParser import RawConfigParser from distutils2.errors import (DistutilsPlatformError, DistutilsFileError, - DistutilsByteCompileError) -from distutils2.spawn import spawn, find_executable + DistutilsByteCompileError, DistutilsExecError) from distutils2 import log from distutils2._backport import sysconfig as _sysconfig @@ -805,3 +805,306 @@ return path.split('\\', 1) else: return path, '' + + +def spawn(cmd, search_path=1, verbose=0, dry_run=0, env=None): + """Run another program specified as a command list 'cmd' in a new process. + + 'cmd' is just the argument list for the new process, ie. + cmd[0] is the program to run and cmd[1:] are the rest of its arguments. + There is no way to run a program with a name different from that of its + executable. + + If 'search_path' is true (the default), the system's executable + search path will be used to find the program; otherwise, cmd[0] + must be the exact path to the executable. If 'dry_run' is true, + the command will not actually be run. + + If 'env' is given, it's a environment dictionary used for the execution + environment. + + Raise DistutilsExecError if running the program fails in any way; just + return on success. + """ + if os.name == 'posix': + _spawn_posix(cmd, search_path, dry_run=dry_run, env=env) + elif os.name == 'nt': + _spawn_nt(cmd, search_path, dry_run=dry_run, env=env) + elif os.name == 'os2': + _spawn_os2(cmd, search_path, dry_run=dry_run, env=env) + else: + raise DistutilsPlatformError( + "don't know how to spawn programs on platform '%s'" % os.name) + + +def _nt_quote_args(args): + """Quote command-line arguments for DOS/Windows conventions. + + Just wraps every argument which contains blanks in double quotes, and + returns a new argument list. + """ + # XXX this doesn't seem very robust to me -- but if the Windows guys + # say it'll work, I guess I'll have to accept it. (What if an arg + # contains quotes? What other magic characters, other than spaces, + # have to be escaped? Is there an escaping mechanism other than + # quoting?) + for i, arg in enumerate(args): + if ' ' in arg: + args[i] = '"%s"' % arg + return args + + +def _spawn_nt(cmd, search_path=1, verbose=0, dry_run=0, env=None): + executable = cmd[0] + cmd = _nt_quote_args(cmd) + if search_path: + # either we find one or it stays the same + executable = find_executable(executable) or executable + log.info(' '.join([executable] + cmd[1:])) + if not dry_run: + # spawn for NT requires a full path to the .exe + try: + if env is None: + rc = os.spawnv(os.P_WAIT, executable, cmd) + else: + rc = os.spawnve(os.P_WAIT, executable, cmd, env) + + except OSError, exc: + # this seems to happen when the command isn't found + raise DistutilsExecError( + "command '%s' failed: %s" % (cmd[0], exc[-1])) + if rc != 0: + # and this reflects the command running but failing + raise DistutilsExecError( + "command '%s' failed with exit status %d" % (cmd[0], rc)) + + +def _spawn_os2(cmd, search_path=1, verbose=0, dry_run=0, env=None): + executable = cmd[0] + if search_path: + # either we find one or it stays the same + executable = find_executable(executable) or executable + log.info(' '.join([executable] + cmd[1:])) + if not dry_run: + # spawnv for OS/2 EMX requires a full path to the .exe + try: + if env is None: + rc = os.spawnv(os.P_WAIT, executable, cmd) + else: + rc = os.spawnve(os.P_WAIT, executable, cmd, env) + + except OSError, exc: + # this seems to happen when the command isn't found + raise DistutilsExecError( + "command '%s' failed: %s" % (cmd[0], exc[-1])) + if rc != 0: + # and this reflects the command running but failing + log.debug("command '%s' failed with exit status %d" % (cmd[0], rc)) + raise DistutilsExecError( + "command '%s' failed with exit status %d" % (cmd[0], rc)) + + +def _spawn_posix(cmd, search_path=1, verbose=0, dry_run=0, env=None): + log.info(' '.join(cmd)) + if dry_run: + return + + if env is None: + exec_fn = search_path and os.execvp or os.execv + else: + exec_fn = search_path and os.execvpe or os.execve + + pid = os.fork() + + if pid == 0: # in the child + try: + if env is None: + exec_fn(cmd[0], cmd) + else: + exec_fn(cmd[0], cmd, env) + except OSError, e: + sys.stderr.write("unable to execute %s: %s\n" % + (cmd[0], e.strerror)) + os._exit(1) + + sys.stderr.write("unable to execute %s for unknown reasons" % cmd[0]) + os._exit(1) + else: # in the parent + # Loop until the child either exits or is terminated by a signal + # (ie. keep waiting if it's merely stopped) + while 1: + try: + pid, status = os.waitpid(pid, 0) + except OSError, exc: + import errno + if exc.errno == errno.EINTR: + continue + raise DistutilsExecError( + "command '%s' failed: %s" % (cmd[0], exc[-1])) + if os.WIFSIGNALED(status): + raise DistutilsExecError( + "command '%s' terminated by signal %d" % \ + (cmd[0], os.WTERMSIG(status))) + + elif os.WIFEXITED(status): + exit_status = os.WEXITSTATUS(status) + if exit_status == 0: + return # hey, it succeeded! + else: + raise DistutilsExecError( + "command '%s' failed with exit status %d" % \ + (cmd[0], exit_status)) + + elif os.WIFSTOPPED(status): + continue + + else: + raise DistutilsExecError( + "unknown error executing '%s': termination status %d" % \ + (cmd[0], status)) + + +def find_executable(executable, path=None): + """Tries to find 'executable' in the directories listed in 'path'. + + A string listing directories separated by 'os.pathsep'; defaults to + os.environ['PATH']. Returns the complete filename or None if not found. + """ + if path is None: + path = os.environ['PATH'] + paths = path.split(os.pathsep) + base, ext = os.path.splitext(executable) + + if (sys.platform == 'win32' or os.name == 'os2') and (ext != '.exe'): + executable = executable + '.exe' + + if not os.path.isfile(executable): + for p in paths: + f = os.path.join(p, executable) + if os.path.isfile(f): + # the file exists, we have a shot at spawn working + return f + return None + else: + return executable + + +DEFAULT_REPOSITORY = 'http://pypi.python.org/pypi' +DEFAULT_REALM = 'pypi' +DEFAULT_PYPIRC = """\ +[distutils] +index-servers = + pypi + +[pypi] +username:%s +password:%s +""" + +def get_pypirc_path(): + """Returns rc file path.""" + return os.path.join(os.path.expanduser('~'), '.pypirc') + + +def generate_pypirc(username, password): + """Creates a default .pypirc file.""" + rc = get_pypirc_path() + f = open(rc, 'w') + try: + f.write(DEFAULT_PYPIRC % (username, password)) + finally: + f.close() + try: + os.chmod(rc, 0600) + except OSError: + # should do something better here + pass + + +def read_pypirc(repository=DEFAULT_REPOSITORY, realm=DEFAULT_REALM): + """Reads the .pypirc file.""" + rc = get_pypirc_path() + if os.path.exists(rc): + config = RawConfigParser() + config.read(rc) + sections = config.sections() + if 'distutils' in sections: + # let's get the list of servers + index_servers = config.get('distutils', 'index-servers') + _servers = [server.strip() for server in + index_servers.split('\n') + if server.strip() != ''] + if _servers == []: + # nothing set, let's try to get the default pypi + if 'pypi' in sections: + _servers = ['pypi'] + else: + # the file is not properly defined, returning + # an empty dict + return {} + for server in _servers: + current = {'server': server} + current['username'] = config.get(server, 'username') + + # optional params + for key, default in (('repository', + DEFAULT_REPOSITORY), + ('realm', DEFAULT_REALM), + ('password', None)): + if config.has_option(server, key): + current[key] = config.get(server, key) + else: + current[key] = default + if (current['server'] == repository or + current['repository'] == repository): + return current + elif 'server-login' in sections: + # old format + server = 'server-login' + if config.has_option(server, 'repository'): + repository = config.get(server, 'repository') + else: + repository = DEFAULT_REPOSITORY + + return {'username': config.get(server, 'username'), + 'password': config.get(server, 'password'), + 'repository': repository, + 'server': server, + 'realm': DEFAULT_REALM} + + return {} + + +def metadata_to_dict(meta): + """XXX might want to move it to the Metadata class.""" + data = { + 'metadata_version' : meta.version, + 'name': meta['Name'], + 'version': meta['Version'], + 'summary': meta['Summary'], + 'home_page': meta['Home-page'], + 'author': meta['Author'], + 'author_email': meta['Author-email'], + 'license': meta['License'], + 'description': meta['Description'], + 'keywords': meta['Keywords'], + 'platform': meta['Platform'], + 'classifier': meta['Classifier'], + 'download_url': meta['Download-URL'], + } + + if meta.version == '1.2': + data['requires_dist'] = meta['Requires-Dist'] + data['requires_python'] = meta['Requires-Python'] + data['requires_external'] = meta['Requires-External'] + data['provides_dist'] = meta['Provides-Dist'] + data['obsoletes_dist'] = meta['Obsoletes-Dist'] + data['project_url'] = [','.join(url) for url in + meta['Project-URL']] + + elif meta.version == '1.1': + data['provides'] = meta['Provides'] + data['requires'] = meta['Requires'] + data['obsoletes'] = meta['Obsoletes'] + + return data diff --git a/src/tests.sh b/src/tests.sh --- a/src/tests.sh +++ b/src/tests.sh @@ -1,8 +1,9 @@ #!/bin/sh echo -n "Running tests for Python 2.4... " -rm -rf _hashlib.so +rm -rf *.so python2.4 setup.py build_ext -i -q 2> /dev/null > /dev/null python2.4 -Wd runtests.py -q 2> /dev/null +rm -rf *.so if [ $? -ne 0 ];then echo "Failed" exit 1 @@ -11,9 +12,9 @@ fi echo -n "Running tests for Python 2.5... " -rm -rf _hashlib.so python2.5 setup.py build_ext -i -q 2> /dev/null > /dev/null python2.5 -Wd runtests.py -q 2> /dev/null +rm -rf *.so if [ $? -ne 0 ];then echo "Failed" exit 1 -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:47 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:47 +0200 Subject: [Python-checkins] distutils2: Improve Metadata instantiation Message-ID: tarek.ziade pushed fdd3e43969eb to distutils2: http://hg.python.org/distutils2/rev/fdd3e43969eb changeset: 477:fdd3e43969eb user: ?ric Araujo date: Thu Aug 05 17:25:42 2010 +0200 summary: Improve Metadata instantiation files: src/distutils2/metadata.py, src/distutils2/tests/test_metadata.py diff --git a/src/distutils2/metadata.py b/src/distutils2/metadata.py --- a/src/distutils2/metadata.py +++ b/src/distutils2/metadata.py @@ -182,23 +182,30 @@ class DistributionMetadata(object): """The metadata of a release. - Supports versions 1.0, 1.1 and 1.2 (auto-detected). + Supports versions 1.0, 1.1 and 1.2 (auto-detected). You can + instantiate the class with one of these arguments (or none): + - *path*, the path to a METADATA file + - *fileobj* give a file-like object with METADATA as content + - *mapping* is a dict-like object + """ + # TODO document that execution_context and platform_dependent are used + # to filter on query, not when setting a key + # also document the mapping API and UNKNOWN default key - if from_dict attribute is set, all key/values pairs will be sent to the - "set" method, building the metadata from the dict. - """ def __init__(self, path=None, platform_dependent=False, execution_context=None, fileobj=None, mapping=None): self._fields = {} self.version = None self.docutils_support = _HAS_DOCUTILS self.platform_dependent = platform_dependent + self.execution_context = execution_context + if [path, fileobj, mapping].count(None) < 2: + raise TypeError('path, fileobj and mapping are exclusive') if path is not None: self.read(path) elif fileobj is not None: self.read_file(fileobj) - self.execution_context = execution_context - if mapping: + elif mapping is not None: self.update(mapping) def _set_best_version(self): @@ -329,7 +336,7 @@ def update(self, other=None, **kwargs): """Set metadata values from the given mapping - + Convert the keys to Metadata fields. Given keys that don't match a metadata argument will not be used. diff --git a/src/distutils2/tests/test_metadata.py b/src/distutils2/tests/test_metadata.py --- a/src/distutils2/tests/test_metadata.py +++ b/src/distutils2/tests/test_metadata.py @@ -13,6 +13,36 @@ class DistributionMetadataTestCase(LoggingSilencer, unittest.TestCase): + def test_instantiation(self): + PKG_INFO = os.path.join(os.path.dirname(__file__), 'PKG-INFO') + fp = open(PKG_INFO) + try: + contents = fp.read() + finally: + fp.close() + fp = StringIO(contents) + + m = DistributionMetadata() + self.assertRaises(MetadataUnrecognizedVersionError, m.items) + + m = DistributionMetadata(PKG_INFO) + self.assertEqual(len(m.items()), 22) + + m = DistributionMetadata(fileobj=fp) + self.assertEqual(len(m.items()), 22) + + m = DistributionMetadata(mapping=dict(name='Test', version='1.0')) + self.assertEqual(len(m.items()), 11) + + d = dict(m.items()) + self.assertRaises(TypeError, DistributionMetadata, + PKG_INFO, fileobj=fp) + self.assertRaises(TypeError, DistributionMetadata, + PKG_INFO, mapping=d) + self.assertRaises(TypeError, DistributionMetadata, + fileobj=fp, mapping=d) + self.assertRaises(TypeError, DistributionMetadata, + PKG_INFO, mapping=m, fileobj=fp) def test_interpret(self): sys_platform = sys.platform @@ -114,15 +144,15 @@ metadata.read_file(out) self.assertEqual(wanted, metadata['Description']) - def test_mapper_apis(self): + def test_mapping_api(self): PKG_INFO = os.path.join(os.path.dirname(__file__), 'PKG-INFO') content = open(PKG_INFO).read() content = content % sys.platform - metadata = DistributionMetadata() - metadata.read_file(StringIO(content)) + metadata = DistributionMetadata(fileobj=StringIO(content)) self.assertIn('Version', metadata.keys()) self.assertIn('0.5', metadata.values()) self.assertIn(('Version', '0.5'), metadata.items()) + #TODO test update def test_versions(self): metadata = DistributionMetadata() -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:47 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:47 +0200 Subject: [Python-checkins] distutils2: Fix merge error Message-ID: tarek.ziade pushed 7debb069fc21 to distutils2: http://hg.python.org/distutils2/rev/7debb069fc21 changeset: 479:7debb069fc21 user: ?ric Araujo date: Thu Aug 05 18:23:21 2010 +0200 summary: Fix merge error files: src/distutils2/tests/pypi_server.py diff --git a/src/distutils2/tests/pypi_server.py b/src/distutils2/tests/pypi_server.py --- a/src/distutils2/tests/pypi_server.py +++ b/src/distutils2/tests/pypi_server.py @@ -330,7 +330,7 @@ 'licence': self.licence, 'summary': self.summary, 'home_page': self.homepage, - 'stable_version': self.stale_version, + 'stable_version': self.stable_version, 'provides_dist': self.provides_dist, 'requires': self.requires, 'cheesecake_installability_id': self.cheesecake_installability_id, -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:47 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:47 +0200 Subject: [Python-checkins] distutils2: removed conditional import on 'warnings' Message-ID: tarek.ziade pushed 5c2ff2b730f7 to distutils2: http://hg.python.org/distutils2/rev/5c2ff2b730f7 changeset: 484:5c2ff2b730f7 user: Yannick Gingras date: Thu Aug 05 19:37:39 2010 -0400 summary: removed conditional import on 'warnings' files: src/distutils2/dist.py diff --git a/src/distutils2/dist.py b/src/distutils2/dist.py --- a/src/distutils2/dist.py +++ b/src/distutils2/dist.py @@ -7,11 +7,7 @@ __revision__ = "$Id: dist.py 77717 2010-01-24 00:33:32Z tarek.ziade $" import sys, os, re - -try: - import warnings -except ImportError: - warnings = None +import warnings from ConfigParser import RawConfigParser @@ -251,10 +247,7 @@ setattr(self, key, val) else: msg = "Unknown distribution option: %r" % key - if warnings is not None: - warnings.warn(msg) - else: - sys.stderr.write(msg + "\n") + warnings.warn(msg) # no-user-cfg is handled before other command line args # because other args override the config files, and this -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:47 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:47 +0200 Subject: [Python-checkins] distutils2: added a test for Distribution(attrs={'options'=...}) Message-ID: tarek.ziade pushed bda950874d27 to distutils2: http://hg.python.org/distutils2/rev/bda950874d27 changeset: 481:bda950874d27 parent: 392:689ec6a55b14 user: Yannick Gingras date: Fri Jul 30 00:38:16 2010 -0400 summary: added a test for Distribution(attrs={'options'=...}) files: src/distutils2/tests/test_dist.py diff --git a/src/distutils2/tests/test_dist.py b/src/distutils2/tests/test_dist.py --- a/src/distutils2/tests/test_dist.py +++ b/src/distutils2/tests/test_dist.py @@ -176,6 +176,21 @@ self.assertEqual(len(warns), 0) + def test_non_empty_options(self): + # TODO: how to actually use options is not documented except + # for a few cryptic comments in dist.py. If this is to stay + # in the public API, it deserves some better documentation. + + # Here is an example of how it's used out there: + # http://svn.pythonmac.org/py2app/py2app/trunk/doc/index.html#specifying-customizations + cls = Distribution + dist = cls(attrs={'author': 'xxx', + 'name': 'xxx', + 'version': 'xxx', + 'url': 'xxxx', + 'options': dict(sdist=dict(owner="root"))}) + self.assertTrue("owner" in dist.get_option_dict("sdist")) + def test_finalize_options(self): attrs = {'keywords': 'one,two', -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:47 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:47 +0200 Subject: [Python-checkins] distutils2: One rename is done Message-ID: tarek.ziade pushed 06da7c8c1f66 to distutils2: http://hg.python.org/distutils2/rev/06da7c8c1f66 changeset: 480:06da7c8c1f66 user: ?ric Araujo date: Thu Aug 05 19:19:22 2010 +0200 summary: One rename is done files: src/DEVNOTES.txt diff --git a/src/DEVNOTES.txt b/src/DEVNOTES.txt --- a/src/DEVNOTES.txt +++ b/src/DEVNOTES.txt @@ -17,6 +17,5 @@ - DistributionMetadata > Metadata or ReleaseMetadata - pkgutil > pkgutil.__init__ + new pkgutil.database (or better name) - - pypi > index - RationalizedVersion > Version - suggest_rationalized_version > suggest -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:47 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:47 +0200 Subject: [Python-checkins] distutils2: Forgot to convert two literals to os.curdir Message-ID: tarek.ziade pushed 31cf5ffe9bf1 to distutils2: http://hg.python.org/distutils2/rev/31cf5ffe9bf1 changeset: 478:31cf5ffe9bf1 user: ?ric Araujo date: Thu Aug 05 17:26:26 2010 +0200 summary: Forgot to convert two literals to os.curdir files: src/distutils2/tests/__init__.py, src/distutils2/tests/test_cmd.py diff --git a/src/distutils2/tests/__init__.py b/src/distutils2/tests/__init__.py --- a/src/distutils2/tests/__init__.py +++ b/src/distutils2/tests/__init__.py @@ -23,7 +23,7 @@ from test.test_support import TESTFN # use TESTFN from stdlib/test_support. -here = os.path.dirname(__file__) or '.' +here = os.path.dirname(__file__) or os.curdir verbose = 1 diff --git a/src/distutils2/tests/test_cmd.py b/src/distutils2/tests/test_cmd.py --- a/src/distutils2/tests/test_cmd.py +++ b/src/distutils2/tests/test_cmd.py @@ -98,7 +98,7 @@ def test_ensure_dirname(self): cmd = self.cmd - cmd.option1 = os.path.dirname(__file__) or '.' + cmd.option1 = os.path.dirname(__file__) or os.curdir cmd.ensure_dirname('option1') cmd.option2 = 'xxx' self.assertRaises(DistutilsOptionError, cmd.ensure_dirname, 'option2') -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:47 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:47 +0200 Subject: [Python-checkins] distutils2: Fix missing import Message-ID: tarek.ziade pushed 23a9e9264513 to distutils2: http://hg.python.org/distutils2/rev/23a9e9264513 changeset: 476:23a9e9264513 user: ?ric Araujo date: Thu Aug 05 17:25:02 2010 +0200 summary: Fix missing import files: src/distutils2/tests/pypi_server.py diff --git a/src/distutils2/tests/pypi_server.py b/src/distutils2/tests/pypi_server.py --- a/src/distutils2/tests/pypi_server.py +++ b/src/distutils2/tests/pypi_server.py @@ -9,6 +9,7 @@ import Queue import SocketServer import select +import socket import threading from BaseHTTPServer import HTTPServer -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:47 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:47 +0200 Subject: [Python-checkins] distutils2: Merge from Alexis Message-ID: tarek.ziade pushed 251a40b381cb to distutils2: http://hg.python.org/distutils2/rev/251a40b381cb changeset: 475:251a40b381cb parent: 443:0f543ed42707 parent: 474:437bda319048 user: ?ric Araujo date: Thu Aug 05 16:54:59 2010 +0200 summary: Merge from Alexis files: docs/source/index.rst, docs/source/pypi.rst, src/distutils2/index/dist.py, src/distutils2/index/simple.py, src/distutils2/index/wrapper.py, src/distutils2/index/xmlrpc.py, src/distutils2/metadata.py, src/distutils2/pypi/__init__.py, src/distutils2/pypi/dist.py, src/distutils2/pypi/errors.py, src/distutils2/pypi/simple.py, src/distutils2/tests/pypi_server.py, src/distutils2/tests/test_index_dist.py, src/distutils2/tests/test_index_simple.py, src/distutils2/tests/test_pypi_dist.py, src/distutils2/tests/test_pypi_simple.py, src/distutils2/util.py diff --git a/docs/source/index.rst b/docs/source/index.rst --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -17,7 +17,7 @@ commands command_hooks test_framework - pypi + projects-index version Indices and tables diff --git a/docs/source/pypi.rst b/docs/source/projects-index.client.rst rename from docs/source/pypi.rst rename to docs/source/projects-index.client.rst --- a/docs/source/pypi.rst +++ b/docs/source/projects-index.client.rst @@ -1,195 +1,20 @@ -========================================= -Tools to query PyPI: the PyPI package -========================================= +=============================== +High level API to Query indexes +=============================== -Distutils2 comes with a module (eg. `distutils2.pypi`) which contains -facilities to access the Python Package Index (named "pypi", and avalaible on -the url `http://pypi.python.org`. +Distutils2 provides a high level API to query indexes, search for releases and +distributions, no matters the underlying API you want to use. -There is two ways to retrieve data from pypi: using the *simple* API, and using -*XML-RPC*. The first one is in fact a set of HTML pages avalaible at -`http://pypi.python.org/simple/`, and the second one contains a set of XML-RPC -methods. In order to reduce the overload caused by running distant methods on -the pypi server (by using the XML-RPC methods), the best way to retrieve -informations is by using the simple API, when it contains the information you -need. - -Distutils2 provides two python modules to ease the work with those two APIs: -`distutils2.pypi.simple` and `distutils2.pypi.xmlrpc`. Both of them depends on -another python module: `distutils2.pypi.dist`. - - -Requesting information via the "simple" API `distutils2.pypi.simple` -==================================================================== - -`distutils2.pypi.simple` can process the Python Package Index and return and -download urls of distributions, for specific versions or latests, but it also -can process external html pages, with the goal to find *pypi unhosted* versions -of python distributions. - -You should use `distutils2.pypi.simple` for: - - * Search distributions by name and versions. - * Process pypi external pages. - * Download distributions by name and versions. - -And should not be used to: - - * Things that will end up in too long index processing (like "finding all - distributions with a specific version, no matters the name") +The aim of this module is to choose the best way to query the API, using the +less possible XML-RPC, and when possible the simple index. API ----- +=== -Here is a complete overview of the APIs of the SimpleIndex class. +The client comes with the common methods "find_projects", "get_release" and +"get_releases", which helps to query the servers, and returns +:class:`distutils2.index.dist.ReleaseInfo`, and +:class:`distutils2.index.dist.ReleasesList` objects. -.. autoclass:: distutils2.pypi.simple.SimpleIndex +.. autoclass:: distutils2.index.wrapper.ClientWrapper :members: - -Usage Exemples ---------------- - -To help you understand how using the `SimpleIndex` class, here are some basic -usages. - -Request PyPI to get a specific distribution -++++++++++++++++++++++++++++++++++++++++++++ - -Supposing you want to scan the PyPI index to get a list of distributions for -the "foobar" project. You can use the "find" method for that:: - - >>> from distutils2.pypi import SimpleIndex - >>> client = SimpleIndex() - >>> client.find("foobar") - [, ] - -Note that you also can request the client about specific versions, using version -specifiers (described in `PEP 345 -`_):: - - >>> client.find("foobar < 1.2") - [, ] - -`find` returns a list of distributions, but you also can get the last -distribution (the more up to date) that fullfil your requirements, like this:: - - >>> client.get("foobar < 1.2") - - -Download distributions -+++++++++++++++++++++++ - -As it can get the urls of distributions provided by PyPI, the `SimpleIndex` -client also can download the distributions and put it for you in a temporary -destination:: - - >>> client.download("foobar") - /tmp/temp_dir/foobar-1.2.tar.gz - -You also can specify the directory you want to download to:: - - >>> client.download("foobar", "/path/to/my/dir") - /path/to/my/dir/foobar-1.2.tar.gz - -While downloading, the md5 of the archive will be checked, if not matches, it -will try another time, then if fails again, raise `MD5HashDoesNotMatchError`. - -Internally, that's not the SimpleIndex which download the distributions, but the -`PyPIDistribution` class. Please refer to this documentation for more details. - -Following PyPI external links -++++++++++++++++++++++++++++++ - -The default behavior for distutils2 is to *not* follow the links provided -by HTML pages in the "simple index", to find distributions related -downloads. - -It's possible to tell the PyPIClient to follow external links by setting the -`follow_externals` attribute, on instanciation or after:: - - >>> client = SimpleIndex(follow_externals=True) - -or :: - - >>> client = SimpleIndex() - >>> client.follow_externals = True - -Working with external indexes, and mirrors -+++++++++++++++++++++++++++++++++++++++++++ - -The default `SimpleIndex` behavior is to rely on the Python Package index stored -on PyPI (http://pypi.python.org/simple). - -As you can need to work with a local index, or private indexes, you can specify -it using the index_url parameter:: - - >>> client = SimpleIndex(index_url="file://filesystem/path/") - -or :: - - >>> client = SimpleIndex(index_url="http://some.specific.url/") - -You also can specify mirrors to fallback on in case the first index_url you -provided doesnt respond, or not correctly. The default behavior for -`SimpleIndex` is to use the list provided by Python.org DNS records, as -described in the :pep:`381` about mirroring infrastructure. - -If you don't want to rely on these, you could specify the list of mirrors you -want to try by specifying the `mirrors` attribute. It's a simple iterable:: - - >>> mirrors = ["http://first.mirror","http://second.mirror"] - >>> client = SimpleIndex(mirrors=mirrors) - - -Requesting informations via XML-RPC (`distutils2.pypi.XmlRpcIndex`) -========================================================================== - -The other method to request the Python package index, is using the XML-RPC -methods. Distutils2 provides a simple wrapper around `xmlrpclib -`_, that can return you -`PyPIDistribution` objects. - -:: - >>> from distutils2.pypi import XmlRpcIndex() - >>> client = XmlRpcIndex() - - -PyPI Distributions -================== - -Both `SimpleIndex` and `XmlRpcIndex` classes works with the classes provided -in the `pypi.dist` package. - -`PyPIDistribution` ------------------- - -`PyPIDistribution` is a simple class that defines the following attributes: - -:name: - The name of the package. `foobar` in our exemples here -:version: - The version of the package -:location: - If the files from the archive has been downloaded, here is the path where - you can find them. -:url: - The url of the distribution - -.. autoclass:: distutils2.pypi.dist.PyPIDistribution - :members: - -`PyPIDistributions` -------------------- - -The `dist` module also provides another class, to work with lists of -`PyPIDistribution` classes. It allow to filter results and is used as a -container of - -.. autoclass:: distutils2.pypi.dist.PyPIDistributions - :members: - -At a higher level -================= - -XXX : A description about a wraper around PyPI simple and XmlRpc Indexes -(PyPIIndex ?) diff --git a/docs/source/projects-index.dist.rst b/docs/source/projects-index.dist.rst new file mode 100644 --- /dev/null +++ b/docs/source/projects-index.dist.rst @@ -0,0 +1,109 @@ +================================================== +Representation of informations coming from indexes +================================================== + +Informations coming from indexes are represented by the classes present in the +`dist` module. + +APIs +==== + +Keep in mind that each project (eg. FooBar) can have several releases +(eg. 1.1, 1.2, 1.3), and each of these releases can be provided in multiple +distributions (eg. a source distribution, a binary one, etc). + +ReleaseInfo +------------ + +Each release have a project name, a project version and contain project +metadata. In addition, releases contain the distributions too. + +These informations are stored in :class:`distutils2.index.dist.ReleaseInfo` +objects. + +.. autoclass:: distutils2.index.dist.ReleaseInfo + :members: + +DistInfo +--------- + +:class:`distutils2.index.dist.DistInfo` is a simple class that contains +informations related to distributions. It's mainly about the URLs where those +distributions can be found. + +.. autoclass:: distutils2.index.dist.DistInfo + :members: + +ReleasesList +------------ + +The `dist` module also provides another class, to work with lists of +:class:`distutils.index.dist.ReleaseInfo` classes. It allow to filter +and order results. + +.. autoclass:: distutils2.index.dist.ReleasesList + :members: + +Exemple usages +=============== + +Build a list of releases, and order them +---------------------------------------- + +Assuming we have a list of releases:: + + >>> from distutils2.index.dist import ReleaseList, ReleaseInfo + >>> fb10 = ReleaseInfo("FooBar", "1.0") + >>> fb11 = ReleaseInfo("FooBar", "1.1") + >>> fb11a = ReleaseInfo("FooBar", "1.1a1") + >>> ReleasesList("FooBar", [fb11, fb11a, fb10]) + >>> releases.sort_releases() + >>> releases.get_versions() + ['1.1', '1.1a1', '1.0'] + >>> releases.add_release("1.2a1") + >>> releases.get_versions() + ['1.1', '1.1a1', '1.0', '1.2a1'] + >>> releases.sort_releases() + ['1.2a1', '1.1', '1.1a1', '1.0'] + >>> releases.sort_releases(prefer_final=True) + >>> releases.get_versions() + ['1.1', '1.0', '1.2a1', '1.1a1'] + + +Add distribution related informations to releases +------------------------------------------------- + +It's easy to add distribution informatons to releases:: + + >>> from distutils2.index.dist import ReleaseList, ReleaseInfo + >>> r = ReleaseInfo("FooBar", "1.0") + >>> r.add_distribution("sdist", url="http://example.org/foobar-1.0.tar.gz") + >>> r.dists + {'sdist': FooBar 1.0 sdist} + >>> r['sdist'].url + {'url': 'http://example.org/foobar-1.0.tar.gz', 'hashname': None, 'hashval': + None, 'is_external': True} + +Attributes Lazy loading +----------------------- + +To abstract a maximum the way of querying informations to the indexes, +attributes and releases informations can be retrieved "on demand", in a "lazy" +way. + +For instance, if you have a release instance that does not contain the metadata +attribute, it can be build directly when accedded:: + + >>> r = Release("FooBar", "1.1") + >>> print r._metadata + None # metadata field is actually set to "None" + >>> r.metadata + + +Like this, it's possible to retrieve project's releases, releases metadata and +releases distributions informations. + +Internally, this is possible because while retrieving for the first time +informations about projects, releases or distributions, a reference to the +client used is stored in the objects. Then, while trying to access undefined +fields, it will be used if necessary. diff --git a/docs/source/pypi.rst b/docs/source/projects-index.rst copy from docs/source/pypi.rst copy to docs/source/projects-index.rst --- a/docs/source/pypi.rst +++ b/docs/source/projects-index.rst @@ -1,195 +1,28 @@ -========================================= -Tools to query PyPI: the PyPI package -========================================= +=================================== +Query Python Package Indexes (PyPI) +=================================== -Distutils2 comes with a module (eg. `distutils2.pypi`) which contains -facilities to access the Python Package Index (named "pypi", and avalaible on -the url `http://pypi.python.org`. +Distutils2 provides facilities to access python package informations stored in +indexes. The main Python Package Index is available at http://pypi.python.org. -There is two ways to retrieve data from pypi: using the *simple* API, and using -*XML-RPC*. The first one is in fact a set of HTML pages avalaible at +.. note:: The tools provided in distutils2 are not limited to query pypi, and + can be used for others indexes, if they respect the same interfaces. + +There is two ways to retrieve data from these indexes: using the *simple* API, +and using *XML-RPC*. The first one is a set of HTML pages avalaibles at `http://pypi.python.org/simple/`, and the second one contains a set of XML-RPC -methods. In order to reduce the overload caused by running distant methods on -the pypi server (by using the XML-RPC methods), the best way to retrieve -informations is by using the simple API, when it contains the information you -need. +methods. -Distutils2 provides two python modules to ease the work with those two APIs: -`distutils2.pypi.simple` and `distutils2.pypi.xmlrpc`. Both of them depends on -another python module: `distutils2.pypi.dist`. +If you dont care about which API to use, the best thing to do is to let +distutils2 decide this for you, by using :class:`distutils2.index.ClientWrapper`. +Of course, you can rely too on :class:`distutils2.index.simple.Crawler` and +:class:`distutils.index.xmlrpc.Client` if you need to use these specific APIs. -Requesting information via the "simple" API `distutils2.pypi.simple` -==================================================================== +.. toctree:: + :maxdepth: 2 -`distutils2.pypi.simple` can process the Python Package Index and return and -download urls of distributions, for specific versions or latests, but it also -can process external html pages, with the goal to find *pypi unhosted* versions -of python distributions. - -You should use `distutils2.pypi.simple` for: - - * Search distributions by name and versions. - * Process pypi external pages. - * Download distributions by name and versions. - -And should not be used to: - - * Things that will end up in too long index processing (like "finding all - distributions with a specific version, no matters the name") - -API ----- - -Here is a complete overview of the APIs of the SimpleIndex class. - -.. autoclass:: distutils2.pypi.simple.SimpleIndex - :members: - -Usage Exemples ---------------- - -To help you understand how using the `SimpleIndex` class, here are some basic -usages. - -Request PyPI to get a specific distribution -++++++++++++++++++++++++++++++++++++++++++++ - -Supposing you want to scan the PyPI index to get a list of distributions for -the "foobar" project. You can use the "find" method for that:: - - >>> from distutils2.pypi import SimpleIndex - >>> client = SimpleIndex() - >>> client.find("foobar") - [, ] - -Note that you also can request the client about specific versions, using version -specifiers (described in `PEP 345 -`_):: - - >>> client.find("foobar < 1.2") - [, ] - -`find` returns a list of distributions, but you also can get the last -distribution (the more up to date) that fullfil your requirements, like this:: - - >>> client.get("foobar < 1.2") - - -Download distributions -+++++++++++++++++++++++ - -As it can get the urls of distributions provided by PyPI, the `SimpleIndex` -client also can download the distributions and put it for you in a temporary -destination:: - - >>> client.download("foobar") - /tmp/temp_dir/foobar-1.2.tar.gz - -You also can specify the directory you want to download to:: - - >>> client.download("foobar", "/path/to/my/dir") - /path/to/my/dir/foobar-1.2.tar.gz - -While downloading, the md5 of the archive will be checked, if not matches, it -will try another time, then if fails again, raise `MD5HashDoesNotMatchError`. - -Internally, that's not the SimpleIndex which download the distributions, but the -`PyPIDistribution` class. Please refer to this documentation for more details. - -Following PyPI external links -++++++++++++++++++++++++++++++ - -The default behavior for distutils2 is to *not* follow the links provided -by HTML pages in the "simple index", to find distributions related -downloads. - -It's possible to tell the PyPIClient to follow external links by setting the -`follow_externals` attribute, on instanciation or after:: - - >>> client = SimpleIndex(follow_externals=True) - -or :: - - >>> client = SimpleIndex() - >>> client.follow_externals = True - -Working with external indexes, and mirrors -+++++++++++++++++++++++++++++++++++++++++++ - -The default `SimpleIndex` behavior is to rely on the Python Package index stored -on PyPI (http://pypi.python.org/simple). - -As you can need to work with a local index, or private indexes, you can specify -it using the index_url parameter:: - - >>> client = SimpleIndex(index_url="file://filesystem/path/") - -or :: - - >>> client = SimpleIndex(index_url="http://some.specific.url/") - -You also can specify mirrors to fallback on in case the first index_url you -provided doesnt respond, or not correctly. The default behavior for -`SimpleIndex` is to use the list provided by Python.org DNS records, as -described in the :pep:`381` about mirroring infrastructure. - -If you don't want to rely on these, you could specify the list of mirrors you -want to try by specifying the `mirrors` attribute. It's a simple iterable:: - - >>> mirrors = ["http://first.mirror","http://second.mirror"] - >>> client = SimpleIndex(mirrors=mirrors) - - -Requesting informations via XML-RPC (`distutils2.pypi.XmlRpcIndex`) -========================================================================== - -The other method to request the Python package index, is using the XML-RPC -methods. Distutils2 provides a simple wrapper around `xmlrpclib -`_, that can return you -`PyPIDistribution` objects. - -:: - >>> from distutils2.pypi import XmlRpcIndex() - >>> client = XmlRpcIndex() - - -PyPI Distributions -================== - -Both `SimpleIndex` and `XmlRpcIndex` classes works with the classes provided -in the `pypi.dist` package. - -`PyPIDistribution` ------------------- - -`PyPIDistribution` is a simple class that defines the following attributes: - -:name: - The name of the package. `foobar` in our exemples here -:version: - The version of the package -:location: - If the files from the archive has been downloaded, here is the path where - you can find them. -:url: - The url of the distribution - -.. autoclass:: distutils2.pypi.dist.PyPIDistribution - :members: - -`PyPIDistributions` -------------------- - -The `dist` module also provides another class, to work with lists of -`PyPIDistribution` classes. It allow to filter results and is used as a -container of - -.. autoclass:: distutils2.pypi.dist.PyPIDistributions - :members: - -At a higher level -================= - -XXX : A description about a wraper around PyPI simple and XmlRpc Indexes -(PyPIIndex ?) + projects-index.client.rst + projects-index.dist.rst + projects-index.simple.rst + projects-index.xmlrpc.rst diff --git a/docs/source/projects-index.simple.rst b/docs/source/projects-index.simple.rst new file mode 100644 --- /dev/null +++ b/docs/source/projects-index.simple.rst @@ -0,0 +1,131 @@ +========================================= +Querying indexes via the simple index API +========================================= + +`distutils2.index.simple` can process Python Package Indexes, and provides +useful informations about distributions. It also can crawl local indexes, for +instance. + +You should use `distutils2.index.simple` for: + + * Search distributions by name and versions. + * Process index external pages. + * Download distributions by name and versions. + +And should not be used to: + + * Things that will end up in too long index processing (like "finding all + distributions with a specific version, no matters the name") + +API +--- + +.. autoclass:: distutils2.index.simple.Crawler + :members: + + +Usage Exemples +--------------- + +To help you understand how using the `Crawler` class, here are some basic +usages. + +Request the simple index to get a specific distribution +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +Supposing you want to scan an index to get a list of distributions for +the "foobar" project. You can use the "get_releases" method for that. +The get_releases method will browse the project page, and return :class:`ReleaseInfo` +objects for each found link that rely on downloads. :: + + >>> from distutils2.index.simple import Crawler + >>> crawler = Crawler() + >>> crawler.get_releases("FooBar") + [, ] + +Note that you also can request the client about specific versions, using version +specifiers (described in `PEP 345 +`_):: + + >>> client.get_releases("FooBar < 1.2") + [, ] + +`get_releases` returns a list of :class:`ReleaseInfo`, but you also can get the best +distribution that fullfil your requirements, using "get_release":: + + >>> client.get_release("FooBar < 1.2") + + +Download distributions ++++++++++++++++++++++++ + +As it can get the urls of distributions provided by PyPI, the `Crawler` +client also can download the distributions and put it for you in a temporary +destination:: + + >>> client.download("foobar") + /tmp/temp_dir/foobar-1.2.tar.gz + +You also can specify the directory you want to download to:: + + >>> client.download("foobar", "/path/to/my/dir") + /path/to/my/dir/foobar-1.2.tar.gz + +While downloading, the md5 of the archive will be checked, if not matches, it +will try another time, then if fails again, raise `MD5HashDoesNotMatchError`. + +Internally, that's not the Crawler which download the distributions, but the +`DistributionInfo` class. Please refer to this documentation for more details. + +Following PyPI external links +++++++++++++++++++++++++++++++ + +The default behavior for distutils2 is to *not* follow the links provided +by HTML pages in the "simple index", to find distributions related +downloads. + +It's possible to tell the PyPIClient to follow external links by setting the +`follow_externals` attribute, on instanciation or after:: + + >>> client = Crawler(follow_externals=True) + +or :: + + >>> client = Crawler() + >>> client.follow_externals = True + +Working with external indexes, and mirrors ++++++++++++++++++++++++++++++++++++++++++++ + +The default `Crawler` behavior is to rely on the Python Package index stored +on PyPI (http://pypi.python.org/simple). + +As you can need to work with a local index, or private indexes, you can specify +it using the index_url parameter:: + + >>> client = Crawler(index_url="file://filesystem/path/") + +or :: + + >>> client = Crawler(index_url="http://some.specific.url/") + +You also can specify mirrors to fallback on in case the first index_url you +provided doesnt respond, or not correctly. The default behavior for +`Crawler` is to use the list provided by Python.org DNS records, as +described in the :pep:`381` about mirroring infrastructure. + +If you don't want to rely on these, you could specify the list of mirrors you +want to try by specifying the `mirrors` attribute. It's a simple iterable:: + + >>> mirrors = ["http://first.mirror","http://second.mirror"] + >>> client = Crawler(mirrors=mirrors) + +Searching in the simple index ++++++++++++++++++++++++++++++ + +It's possible to search for projects with specific names in the package index. +Assuming you want to find all projects containing the "Grail" keyword:: + + >>> client.search(name="grail") + ["holy grail", "unholy grail", "grail"] + diff --git a/docs/source/projects-index.xmlrpc.rst b/docs/source/projects-index.xmlrpc.rst new file mode 100644 --- /dev/null +++ b/docs/source/projects-index.xmlrpc.rst @@ -0,0 +1,149 @@ +========================= +Query indexes via XML-RPC +========================= + +Indexes can be queried using XML-RPC calls, and Distutils2 provides a simple +way to interface with XML-RPC. + +You should **use** XML-RPC when: + + * Searching the index for projects **on other fields than project + names**. For instance, you can search for projects based on the + author_email field. + * Searching all the versions that have existed for a project. + * you want to retrive METADATAs informations from releases or + distributions. + +You should **avoid using** XML-RPC method calls when: + + * Retrieving the last version of a project + * Getting the projects with a specific name and version. + * The simple index can match your needs + +When dealing with indexes, keep in mind that the index queriers will always +return you :class:`distutils2.index.ReleaseInfo` and +:class:`distutils2.index.ReleasesList` objects. + +Some methods here share common APIs with the one you can find on +:class:`distutils2.index.simple`, internally, :class:`distutils2.index.client` +is inherited by :class:`distutils2.index.xmlrpc.Client` + +API +==== + +.. autoclass:: distutils2.index.xmlrpc.Client + :members: + +Usage examples +=============== + +Use case described here are use case that are not common to the other clients. +If you want to see all the methods, please refer to API or to usage examples +described in :class:`distutils2.index.client.Client` + +Finding releases +---------------- + +It's a common use case to search for "things" within the index. +We can basically search for projects by their name, which is the +most used way for users (eg. "give me the last version of the FooBar project"). +This can be accomplished using the following syntax:: + + >>> client = xmlrpc.Client() + >>> client.get_release("Foobar (<= 1.3)) + + >>> client.get_releases("FooBar (<= 1.3)") + [FooBar 1.1, FooBar 1.1.1, FooBar 1.2, FooBar 1.2.1] + +And we also can find for specific fields:: + + >>> client.search_projects(field=value) + +You could specify the operator to use, default is "or":: + + >>> client.search_projects(field=value, operator="and") + +The specific fields you can search are: + + * name + * version + * author + * author_email + * maintainer + * maintainer_email + * home_page + * license + * summary + * description + * keywords + * platform + * download_url + +Getting metadata informations +----------------------------- + +XML-RPC is a prefered way to retrieve metadata informations from indexes. +It's really simple to do so:: + + >>> client = xmlrpc.Client() + >>> client.get_metadata("FooBar", "1.1") + + +Assuming we already have a :class:`distutils2.index.ReleaseInfo` object defined, +it's possible to pass it ot the xmlrpc client to retrieve and complete it's +metadata:: + + >>> foobar11 = ReleaseInfo("FooBar", "1.1") + >>> client = xmlrpc.Client() + >>> returned_release = client.get_metadata(release=foobar11) + >>> returned_release + + +Get all the releases of a project +--------------------------------- + +To retrieve all the releases for a project, you can build them using +`get_releases`:: + + >>> client = xmlrpc.Client() + >>> client.get_releases("FooBar") + [, , ] + +Get informations about distributions +------------------------------------ + +Indexes have informations about projects, releases **and** distributions. +If you're not familiar with those, please refer to the documentation of +:mod:`distutils2.index.dist`. + +It's possible to retrive informations about distributions, e.g "what are the +existing distributions for this release ? How to retrieve them ?":: + + >>> client = xmlrpc.Client() + >>> release = client.get_distributions("FooBar", "1.1") + >>> release.dists + {'sdist': , 'bdist': } + +As you see, this does not return a list of distributions, but a release, +because a release can be used like a list of distributions. + +Lazy load information from project, releases and distributions. +---------------------------------------------------------------- + +.. note:: The lazy loading feature is not currently available ! + +As :mod:`distutils2.index.dist` classes support "lazy" loading of +informations, you can use it while retrieving informations from XML-RPC. + +For instance, it's possible to get all the releases for a project, and to access +directly the metadata of each release, without making +:class:`distutils2.index.xmlrpc.Client` directly (they will be made, but they're +invisible to the you):: + + >>> client = xmlrpc.Client() + >>> releases = client.get_releases("FooBar") + >>> releases.get_release("1.1").metadata + + +Refer to the :mod:`distutils2.index.dist` documentation for more information +about attributes lazy loading. diff --git a/src/distutils2/pypi/__init__.py b/src/distutils2/index/__init__.py rename from src/distutils2/pypi/__init__.py rename to src/distutils2/index/__init__.py --- a/src/distutils2/pypi/__init__.py +++ b/src/distutils2/index/__init__.py @@ -1,8 +1,11 @@ -"""distutils2.pypi +"""Package containing ways to interact with Index APIs. -Package containing ways to interact with the PyPI APIs. -""" +""" __all__ = ['simple', + 'xmlrpc', 'dist', -] + 'errors', + 'mirrors',] + +from dist import ReleaseInfo, ReleasesList, DistInfo diff --git a/src/distutils2/index/base.py b/src/distutils2/index/base.py new file mode 100644 --- /dev/null +++ b/src/distutils2/index/base.py @@ -0,0 +1,55 @@ +from distutils2.version import VersionPredicate +from distutils2.index.dist import ReleasesList + + +class BaseClient(object): + """Base class containing common methods for the index crawlers/clients""" + + def __init__(self, prefer_final, prefer_source): + self._prefer_final = prefer_final + self._prefer_source = prefer_source + self._index = self + + def _get_version_predicate(self, requirements): + """Return a VersionPredicate object, from a string or an already + existing object. + """ + if isinstance(requirements, str): + requirements = VersionPredicate(requirements) + return requirements + + def _get_prefer_final(self, prefer_final=None): + """Return the prefer_final internal parameter or the specified one if + provided""" + if prefer_final: + return prefer_final + else: + return self._prefer_final + + def _get_prefer_source(self, prefer_source=None): + """Return the prefer_source internal parameter or the specified one if + provided""" + if prefer_source: + return prefer_source + else: + return self._prefer_source + + def _get_project(self, project_name): + """Return an project instance, create it if necessary""" + return self._projects.setdefault(project_name.lower(), + ReleasesList(project_name, index=self._index)) + + def download_distribution(self, requirements, temp_path=None, + prefer_source=None, prefer_final=None): + """Download a distribution from the last release according to the + requirements. + + If temp_path is provided, download to this path, otherwise, create a + temporary location for the download and return it. + """ + prefer_final = self._get_prefer_final(prefer_final) + prefer_source = self._get_prefer_source(prefer_source) + release = self.get_release(requirements, prefer_final) + if release: + dist = release.get_distribution(prefer_source=prefer_source) + return dist.download(temp_path) diff --git a/src/distutils2/pypi/dist.py b/src/distutils2/index/dist.py rename from src/distutils2/pypi/dist.py rename to src/distutils2/index/dist.py --- a/src/distutils2/pypi/dist.py +++ b/src/distutils2/index/dist.py @@ -1,186 +1,187 @@ -"""distutils2.pypi.dist +"""distutils2.index.dist -Provides the PyPIDistribution class thats represents a distribution retrieved -on PyPI. +Provides useful classes to represent the release and distributions retrieved +from indexes. + +A project can have several releases (=versions) and each release can have +several distributions (sdist, bdist). + +The release contains the metadata related informations (see PEP 384), and the +distributions contains download related informations. + """ +import mimetypes import re +import tarfile +import tempfile +import urllib import urlparse -import urllib -import tempfile -from operator import attrgetter +import zipfile try: import hashlib except ImportError: from distutils2._backport import hashlib +from distutils2.errors import IrrationalVersionError +from distutils2.index.errors import (HashDoesNotMatch, UnsupportedHashName, + CantParseArchiveName) from distutils2.version import suggest_normalized_version, NormalizedVersion -from distutils2.pypi.errors import HashDoesNotMatch, UnsupportedHashName +from distutils2.metadata import DistributionMetadata +from distutils2.util import untar_file, unzip_file, splitext + +__all__ = ['ReleaseInfo', 'DistInfo', 'ReleasesList', 'get_infos_from_url'] EXTENSIONS = ".tar.gz .tar.bz2 .tar .zip .tgz .egg".split() MD5_HASH = re.compile(r'^.*#md5=([a-f0-9]+)$') +DIST_TYPES = ['bdist', 'sdist'] -class PyPIDistribution(object): - """Represents a distribution retrieved from PyPI. +class IndexReference(object): + def set_index(self, index=None): + self._index = index - This is a simple container for various attributes as name, version, - downloaded_location, url etc. - The PyPIDistribution class is used by the pypi.*Index class to return - information about distributions. +class ReleaseInfo(IndexReference): + """Represent a release of a project (a project with a specific version). + The release contain the _metadata informations related to this specific + version, and is also a container for distribution related informations. + + See the DistInfo class for more information about distributions. """ - @classmethod - def from_url(cls, url, probable_dist_name=None, is_external=True): - """Build a Distribution from a url archive (egg or zip or tgz). - - :param url: complete url of the distribution - :param probable_dist_name: A probable name of the distribution. - :param is_external: Tell if the url commes from an index or from - an external URL. + def __init__(self, name, version, metadata=None, hidden=False, + index=None, **kwargs): """ - # if the url contains a md5 hash, get it. - md5_hash = None - match = MD5_HASH.match(url) - if match is not None: - md5_hash = match.group(1) - # remove the hash - url = url.replace("#md5=%s" % md5_hash, "") - - # parse the archive name to find dist name and version - archive_name = urlparse.urlparse(url)[2].split('/')[-1] - extension_matched = False - # remove the extension from the name - for ext in EXTENSIONS: - if archive_name.endswith(ext): - archive_name = archive_name[:-len(ext)] - extension_matched = True - - name, version = split_archive_name(archive_name) - if extension_matched is True: - return PyPIDistribution(name, version, url=url, url_hashname="md5", - url_hashval=md5_hash, - url_is_external=is_external) - - def __init__(self, name, version, type=None, url=None, url_hashname=None, - url_hashval=None, url_is_external=True): - """Create a new instance of PyPIDistribution. - :param name: the name of the distribution :param version: the version of the distribution - :param type: the type of the dist (eg. source, bin-*, etc.) - :param url: URL where we found this distribution - :param url_hashname: the name of the hash we want to use. Refer to the - hashlib.new documentation for more information. - :param url_hashval: the hash value. - :param url_is_external: we need to know if the provided url comes from an - index browsing, or from an external resource. + :param metadata: the metadata fields of the release. + :type metadata: dict + :param kwargs: optional arguments for a new distribution. + """ + self.set_index(index) + self.name = name + self._version = None + self.version = version + if metadata: + self._metadata = DistributionMetadata(mapping=metadata) + else: + self._metadata = None + self._dists = {} + self.hidden = hidden - """ - self.name = name - self.version = NormalizedVersion(version) - self.type = type - # set the downloaded path to None by default. The goal here - # is to not download distributions multiple times - self.downloaded_location = None - # We store urls in dict, because we need to have a bit more informations - # than the simple URL. It will be used later to find the good url to - # use. - # We have two _url* attributes: _url and _urls. _urls contains a list of - # dict for the different urls, and _url contains the choosen url, in - # order to dont make the selection process multiple times. - self._urls = [] - self._url = None - self.add_url(url, url_hashname, url_hashval, url_is_external) + if 'dist_type' in kwargs: + dist_type = kwargs.pop('dist_type') + self.add_distribution(dist_type, **kwargs) - def add_url(self, url, hashname=None, hashval=None, is_external=True): - """Add a new url to the list of urls""" - if hashname is not None: - try: - hashlib.new(hashname) - except ValueError: - raise UnsupportedHashName(hashname) + def set_version(self, version): + try: + self._version = NormalizedVersion(version) + except IrrationalVersionError: + suggestion = suggest_normalized_version(version) + if suggestion: + self.version = suggestion + else: + raise IrrationalVersionError(version) - self._urls.append({ - 'url': url, - 'hashname': hashname, - 'hashval': hashval, - 'is_external': is_external, - }) - # reset the url selection process - self._url = None + def get_version(self): + return self._version + + version = property(get_version, set_version) @property - def url(self): - """Pick up the right url for the list of urls in self.urls""" - # We return internal urls over externals. - # If there is more than one internal or external, return the first - # one. - if self._url is None: - if len(self._urls) > 1: - internals_urls = [u for u in self._urls \ - if u['is_external'] == False] - if len(internals_urls) >= 1: - self._url = internals_urls[0] - if self._url is None: - self._url = self._urls[0] - return self._url - - @property - def is_source(self): - """return if the distribution is a source one or not""" - return self.type == 'source' + def metadata(self): + """If the metadata is not set, use the indexes to get it""" + if not self._metadata: + self._index.get_metadata(self.name, '%s' % self.version) + return self._metadata @property def is_final(self): """proxy to version.is_final""" return self.version.is_final + + @property + def dists(self): + if self._dists is None: + self._index.get_distributions(self.name, '%s' % self.version) + if self._dists is None: + self._dists = {} + return self._dists - def download(self, path=None): - """Download the distribution to a path, and return it. + def add_distribution(self, dist_type='sdist', python_version=None, **params): + """Add distribution informations to this release. + If distribution information is already set for this distribution type, + add the given url paths to the distribution. This can be useful while + some of them fails to download. - If the path is given in path, use this, otherwise, generates a new one + :param dist_type: the distribution type (eg. "sdist", "bdist", etc.) + :param params: the fields to be passed to the distribution object + (see the :class:DistInfo constructor). """ - if path is None: - path = tempfile.mkdtemp() + if dist_type not in DIST_TYPES: + raise ValueError(dist_type) + if dist_type in self.dists: + self._dists[dist_type].add_url(**params) + else: + self._dists[dist_type] = DistInfo(self, dist_type, + index=self._index, **params) + if python_version: + self._dists[dist_type].python_version = python_version - # if we do not have downloaded it yet, do it. - if self.downloaded_location is None: - url = self.url['url'] - archive_name = urlparse.urlparse(url)[2].split('/')[-1] - filename, headers = urllib.urlretrieve(url, - path + "/" + archive_name) - self.downloaded_location = filename - self._check_md5(filename) - return self.downloaded_location + def get_distribution(self, dist_type=None, prefer_source=True): + """Return a distribution. - def _check_md5(self, filename): - """Check that the md5 checksum of the given file matches the one in - url param""" - hashname = self.url['hashname'] - expected_hashval = self.url['hashval'] - if not None in (expected_hashval, hashname): - f = open(filename) - hashval = hashlib.new(hashname) - hashval.update(f.read()) - if hashval.hexdigest() != expected_hashval: - raise HashDoesNotMatch("got %s instead of %s" - % (hashval.hexdigest(), expected_hashval)) + If dist_type is set, find first for this distribution type, and just + act as an alias of __get_item__. - def __repr__(self): - return "%s %s %s %s" \ - % (self.__class__.__name__, self.name, self.version, - self.type or "") + If prefer_source is True, search first for source distribution, and if + not return one existing distribution. + """ + if len(self.dists) == 0: + raise LookupError() + if dist_type: + return self[dist_type] + if prefer_source: + if "sdist" in self.dists: + dist = self["sdist"] + else: + dist = self.dists.values()[0] + return dist + + def download(self, temp_path=None, prefer_source=True): + """Download the distribution, using the requirements. + + If more than one distribution match the requirements, use the last + version. + Download the distribution, and put it in the temp_path. If no temp_path + is given, creates and return one. + + Returns the complete absolute path to the downloaded archive. + """ + return self.get_distribution(prefer_source=prefer_source)\ + .download(path=temp_path) + + def set_metadata(self, metadata): + if not self._metadata: + self._metadata = DistributionMetadata() + self._metadata.update(metadata) + + def __getitem__(self, item): + """distributions are available using release["sdist"]""" + return self.dists[item] def _check_is_comparable(self, other): - if not isinstance(other, PyPIDistribution): + if not isinstance(other, ReleaseInfo): raise TypeError("cannot compare %s and %s" % (type(self).__name__, type(other).__name__)) elif self.name != other.name: raise TypeError("cannot compare %s and %s" % (self.name, other.name)) + def __repr__(self): + return "<%s %s>" % (self.name, self.version) + def __eq__(self, other): self._check_is_comparable(other) return self.version == other.version @@ -205,78 +206,311 @@ __hash__ = object.__hash__ -class PyPIDistributions(list): - """A container of PyPIDistribution objects. +class DistInfo(IndexReference): + """Represents a distribution retrieved from an index (sdist, bdist, ...) + """ - Contains methods and facilities to sort and filter distributions. + def __init__(self, release, dist_type=None, url=None, hashname=None, + hashval=None, is_external=True, python_version=None, + index=None): + """Create a new instance of DistInfo. + + :param release: a DistInfo class is relative to a release. + :param dist_type: the type of the dist (eg. source, bin-*, etc.) + :param url: URL where we found this distribution + :param hashname: the name of the hash we want to use. Refer to the + hashlib.new documentation for more information. + :param hashval: the hash value. + :param is_external: we need to know if the provided url comes from + an index browsing, or from an external resource. + + """ + self.set_index(index) + self.release = release + self.dist_type = dist_type + self.python_version = python_version + self._unpacked_dir = None + # set the downloaded path to None by default. The goal here + # is to not download distributions multiple times + self.downloaded_location = None + # We store urls in dict, because we need to have a bit more infos + # than the simple URL. It will be used later to find the good url to + # use. + # We have two _url* attributes: _url and urls. urls contains a list + # of dict for the different urls, and _url contains the choosen url, in + # order to dont make the selection process multiple times. + self.urls = [] + self._url = None + self.add_url(url, hashname, hashval, is_external) + + def add_url(self, url, hashname=None, hashval=None, is_external=True): + """Add a new url to the list of urls""" + if hashname is not None: + try: + hashlib.new(hashname) + except ValueError: + raise UnsupportedHashName(hashname) + if not url in [u['url'] for u in self.urls]: + self.urls.append({ + 'url': url, + 'hashname': hashname, + 'hashval': hashval, + 'is_external': is_external, + }) + # reset the url selection process + self._url = None + + @property + def url(self): + """Pick up the right url for the list of urls in self.urls""" + # We return internal urls over externals. + # If there is more than one internal or external, return the first + # one. + if self._url is None: + if len(self.urls) > 1: + internals_urls = [u for u in self.urls \ + if u['is_external'] == False] + if len(internals_urls) >= 1: + self._url = internals_urls[0] + if self._url is None: + self._url = self.urls[0] + return self._url + + @property + def is_source(self): + """return if the distribution is a source one or not""" + return self.dist_type == 'sdist' + + def download(self, path=None): + """Download the distribution to a path, and return it. + + If the path is given in path, use this, otherwise, generates a new one + Return the download location. + """ + if path is None: + path = tempfile.mkdtemp() + + # if we do not have downloaded it yet, do it. + if self.downloaded_location is None: + url = self.url['url'] + archive_name = urlparse.urlparse(url)[2].split('/')[-1] + filename, headers = urllib.urlretrieve(url, + path + "/" + archive_name) + self.downloaded_location = filename + self._check_md5(filename) + return self.downloaded_location + + def unpack(self, path=None): + """Unpack the distribution to the given path. + + If not destination is given, creates a temporary location. + + Returns the location of the extracted files (root). + """ + if not self._unpacked_dir: + if path is None: + path = tempfile.mkdtemp() + + filename = self.download() + content_type = mimetypes.guess_type(filename)[0] + + if (content_type == 'application/zip' + or filename.endswith('.zip') + or filename.endswith('.pybundle') + or zipfile.is_zipfile(filename)): + unzip_file(filename, path, flatten=not filename.endswith('.pybundle')) + elif (content_type == 'application/x-gzip' + or tarfile.is_tarfile(filename) + or splitext(filename)[1].lower() in ('.tar', '.tar.gz', '.tar.bz2', '.tgz', '.tbz')): + untar_file(filename, path) + self._unpacked_dir = path + return self._unpacked_dir + + def _check_md5(self, filename): + """Check that the md5 checksum of the given file matches the one in + url param""" + hashname = self.url['hashname'] + expected_hashval = self.url['hashval'] + if not None in (expected_hashval, hashname): + f = open(filename) + hashval = hashlib.new(hashname) + hashval.update(f.read()) + if hashval.hexdigest() != expected_hashval: + raise HashDoesNotMatch("got %s instead of %s" + % (hashval.hexdigest(), expected_hashval)) + + def __repr__(self): + return "<%s %s %s>" % ( + self.release.name, self.release.version, self.dist_type or "") + + +class ReleasesList(IndexReference): + """A container of Release. + + Provides useful methods and facilities to sort and filter releases. """ - def __init__(self, list=[]): - # To disable the ability to pass lists on instanciation - super(PyPIDistributions, self).__init__() - for item in list: - self.append(item) + def __init__(self, name, releases=None, contains_hidden=False, index=None): + self.set_index(index) + self._releases = [] + self.name = name + self.contains_hidden = contains_hidden + if releases: + self.add_releases(releases) + + @property + def releases(self): + if not self._releases: + self.fetch_releases() + return self._releases + + def fetch_releases(self): + self._index.get_releases(self.name) + return self.releases def filter(self, predicate): - """Filter the distributions and return a subset of distributions that - match the given predicate + """Filter and return a subset of releases matching the given predicate. """ - return PyPIDistributions( - [dist for dist in self if dist.name == predicate.name and - predicate.match(dist.version)]) + return ReleasesList(self.name, [release for release in self.releases + if predicate.match(release.version)], + index=self._index) - def get_last(self, predicate, prefer_source=None, prefer_final=None): - """Return the most up to date version, that satisfy the given - predicate + def get_last(self, predicate, prefer_final=None): + """Return the "last" release, that satisfy the given predicates. + + "last" is defined by the version number of the releases, you also could + set prefer_final parameter to True or False to change the order results """ - distributions = self.filter(predicate) - distributions.sort_distributions(prefer_source, prefer_final, reverse=True) - return distributions[0] + releases = self.filter(predicate) + releases.sort_releases(prefer_final, reverse=True) + return releases[0] - def get_same_name_and_version(self): - """Return lists of PyPIDistribution objects that refer to the same - name and version number. This do not consider the type (source, binary, - etc.)""" - processed = [] - duplicates = [] - for dist in self: - if (dist.name, dist.version) not in processed: - processed.append((dist.name, dist.version)) - found_duplicates = [d for d in self if d.name == dist.name and - d.version == dist.version] - if len(found_duplicates) > 1: - duplicates.append(found_duplicates) - return duplicates + def add_releases(self, releases): + """Add releases in the release list. - def append(self, o): - """Append a new distribution to the list. + :param: releases is a list of ReleaseInfo objects. + """ + for r in releases: + self.add_release(release=r) - If a distribution with the same name and version exists, just grab the - URL informations and add a new new url for the existing one. + def add_release(self, version=None, dist_type='sdist', release=None, + **dist_args): + """Add a release to the list. + + The release can be passed in the `release` parameter, and in this case, + it will be crawled to extract the useful informations if necessary, or + the release informations can be directly passed in the `version` and + `dist_type` arguments. + + Other keywords arguments can be provided, and will be forwarded to the + distribution creation (eg. the arguments of the DistInfo constructor). """ - similar_dists = [d for d in self if d.name == o.name and - d.version == o.version and d.type == o.type] - if len(similar_dists) > 0: - dist = similar_dists[0] - dist.add_url(**o.url) + if release: + if release.name.lower() != self.name.lower(): + raise ValueError("%s is not the same project than %s" % + (release.name, self.name)) + version = '%s' % release.version + + if not version in self.get_versions(): + # append only if not already exists + self._releases.append(release) + for dist in release.dists.values(): + for url in dist.urls: + self.add_release(version, dist.dist_type, **url) else: - super(PyPIDistributions, self).append(o) + matches = [r for r in self._releases if '%s' % r.version == version + and r.name == self.name] + if not matches: + release = ReleaseInfo(self.name, version, index=self._index) + self._releases.append(release) + else: + release = matches[0] - def sort_distributions(self, prefer_source=True, prefer_final=False, - reverse=True, *args, **kwargs): - """order the results with the given properties""" + release.add_distribution(dist_type=dist_type, **dist_args) + + def sort_releases(self, prefer_final=False, reverse=True, *args, **kwargs): + """Sort the results with the given properties. + + The `prefer_final` argument can be used to specify if final + distributions (eg. not dev, bet or alpha) would be prefered or not. + + Results can be inverted by using `reverse`. + + Any other parameter provided will be forwarded to the sorted call. You + cannot redefine the key argument of "sorted" here, as it is used + internally to sort the releases. + """ sort_by = [] if prefer_final: sort_by.append("is_final") sort_by.append("version") - if prefer_source: - sort_by.append("is_source") - - super(PyPIDistributions, self).sort( + self.releases.sort( key=lambda i: [getattr(i, arg) for arg in sort_by], reverse=reverse, *args, **kwargs) + def get_release(self, version): + """Return a release from it's version. + """ + matches = [r for r in self.releases if "%s" % r.version == version] + if len(matches) != 1: + raise KeyError(version) + return matches[0] + + def get_versions(self): + """Return a list of releases versions contained""" + return ["%s" % r.version for r in self._releases] + + def __getitem__(self, key): + return self.releases[key] + + def __len__(self): + return len(self.releases) + + def __repr__(self): + string = 'Project "%s"' % self.name + if self.get_versions(): + string += ' versions: %s' % ', '.join(self.get_versions()) + return '<%s>' % string + + +def get_infos_from_url(url, probable_dist_name=None, is_external=True): + """Get useful informations from an URL. + + Return a dict of (name, version, url, hashtype, hash, is_external) + + :param url: complete url of the distribution + :param probable_dist_name: A probable name of the project. + :param is_external: Tell if the url commes from an index or from + an external URL. + """ + # if the url contains a md5 hash, get it. + md5_hash = None + match = MD5_HASH.match(url) + if match is not None: + md5_hash = match.group(1) + # remove the hash + url = url.replace("#md5=%s" % md5_hash, "") + + # parse the archive name to find dist name and version + archive_name = urlparse.urlparse(url)[2].split('/')[-1] + extension_matched = False + # remove the extension from the name + for ext in EXTENSIONS: + if archive_name.endswith(ext): + archive_name = archive_name[:-len(ext)] + extension_matched = True + + name, version = split_archive_name(archive_name) + if extension_matched is True: + return {'name': name, + 'version': version, + 'url': url, + 'hashname': "md5", + 'hashval': md5_hash, + 'is_external': is_external, + 'dist_type': 'sdist'} + def split_archive_name(archive_name, probable_name=None): """Split an archive name into two parts: name and version. @@ -309,7 +543,7 @@ name, version = eager_split(archive_name) version = suggest_normalized_version(version) - if version != "" and name != "": + if version is not None and name != "": return (name.lower(), version) else: raise CantParseArchiveName(archive_name) diff --git a/src/distutils2/pypi/errors.py b/src/distutils2/index/errors.py rename from src/distutils2/pypi/errors.py rename to src/distutils2/index/errors.py --- a/src/distutils2/pypi/errors.py +++ b/src/distutils2/index/errors.py @@ -5,19 +5,27 @@ from distutils2.errors import DistutilsError -class PyPIError(DistutilsError): - """The base class for errors of the pypi python package.""" +class IndexesError(DistutilsError): + """The base class for errors of the index python package.""" -class DistributionNotFound(PyPIError): - """No distribution match the given requirements.""" +class ProjectNotFound(IndexesError): + """Project has not been found""" -class CantParseArchiveName(PyPIError): +class DistributionNotFound(IndexesError): + """The release has not been found""" + + +class ReleaseNotFound(IndexesError): + """The release has not been found""" + + +class CantParseArchiveName(IndexesError): """An archive name can't be parsed to find distribution name and version""" -class DownloadError(PyPIError): +class DownloadError(IndexesError): """An error has occurs while downloading""" @@ -25,9 +33,13 @@ """Compared hashes does not match""" -class UnsupportedHashName(PyPIError): +class UnsupportedHashName(IndexesError): """A unsupported hashname has been used""" -class UnableToDownload(PyPIError): +class UnableToDownload(IndexesError): """All mirrors have been tried, without success""" + + +class InvalidSearchField(IndexesError): + """An invalid search field has been used""" diff --git a/src/distutils2/index/mirrors.py b/src/distutils2/index/mirrors.py new file mode 100644 --- /dev/null +++ b/src/distutils2/index/mirrors.py @@ -0,0 +1,52 @@ +"""Utilities related to the mirror infrastructure defined in PEP 381. +See http://www.python.org/dev/peps/pep-0381/ +""" + +from string import ascii_lowercase +import socket + +DEFAULT_MIRROR_URL = "last.pypi.python.org" + +def get_mirrors(hostname=None): + """Return the list of mirrors from the last record found on the DNS + entry:: + + >>> from distutils2.index.mirrors import get_mirrors + >>> get_mirrors() + ['a.pypi.python.org', 'b.pypi.python.org', 'c.pypi.python.org', + 'd.pypi.python.org'] + + """ + if hostname is None: + hostname = DEFAULT_MIRROR_URL + + # return the last mirror registered on PyPI. + try: + hostname = socket.gethostbyname_ex(hostname)[0] + except socket.gaierror: + return [] + end_letter = hostname.split(".", 1) + + # determine the list from the last one. + return ["%s.%s" % (s, end_letter[1]) for s in string_range(end_letter[0])] + +def string_range(last): + """Compute the range of string between "a" and last. + + This works for simple "a to z" lists, but also for "a to zz" lists. + """ + for k in range(len(last)): + for x in product(ascii_lowercase, repeat=k+1): + result = ''.join(x) + yield result + if result == last: + return + +def product(*args, **kwds): + pools = map(tuple, args) * kwds.get('repeat', 1) + result = [[]] + for pool in pools: + result = [x+[y] for x in result for y in pool] + for prod in result: + yield tuple(prod) + diff --git a/src/distutils2/pypi/simple.py b/src/distutils2/index/simple.py rename from src/distutils2/pypi/simple.py rename to src/distutils2/index/simple.py --- a/src/distutils2/pypi/simple.py +++ b/src/distutils2/index/simple.py @@ -1,6 +1,6 @@ -"""pypi.simple +"""index.simple -Contains the class "SimpleIndex", a simple spider to find and retrieve +Contains the class "SimpleIndexCrawler", a simple spider to find and retrieve distributions on the Python Package Index, using it's "simple" API, avalaible at http://pypi.python.org/simple/ """ @@ -11,17 +11,23 @@ import sys import urllib2 import urlparse +import logging +import os -from distutils2.version import VersionPredicate -from distutils2.pypi.dist import (PyPIDistribution, PyPIDistributions, - EXTENSIONS) -from distutils2.pypi.errors import (PyPIError, DistributionNotFound, - DownloadError, UnableToDownload) +from distutils2.index.base import BaseClient +from distutils2.index.dist import (ReleasesList, EXTENSIONS, + get_infos_from_url, MD5_HASH) +from distutils2.index.errors import (IndexesError, DownloadError, + UnableToDownload, CantParseArchiveName, + ReleaseNotFound, ProjectNotFound) +from distutils2.index.mirrors import get_mirrors +from distutils2.metadata import DistributionMetadata from distutils2 import __version__ as __distutils2_version__ +__all__ = ['Crawler', 'DEFAULT_SIMPLE_INDEX_URL'] + # -- Constants ----------------------------------------------- -PYPI_DEFAULT_INDEX_URL = "http://pypi.python.org/simple/" -PYPI_DEFAULT_MIRROR_URL = "mirrors.pypi.python.org" +DEFAULT_SIMPLE_INDEX_URL = "http://a.pypi.python.org/simple/" DEFAULT_HOSTS = ("*",) SOCKET_TIMEOUT = 15 USER_AGENT = "Python-urllib/%s distutils2/%s" % ( @@ -30,9 +36,6 @@ # -- Regexps ------------------------------------------------- EGG_FRAGMENT = re.compile(r'^egg=([-A-Za-z0-9_.]+)$') HREF = re.compile("""href\\s*=\\s*['"]?([^'"> ]+)""", re.I) -PYPI_MD5 = re.compile( - '([^<]+)\n\s+\\(md5\\)') URL_SCHEME = re.compile('([-+.a-z0-9]{2,}):', re.I).match # This pattern matches a character entity reference (a decimal numeric @@ -58,10 +61,39 @@ return _socket_timeout -class SimpleIndex(object): - """Provides useful tools to request the Python Package Index simple API +def with_mirror_support(): + """Decorator that makes the mirroring support easier""" + def wrapper(func): + def wrapped(self, *args, **kwargs): + try: + return func(self, *args, **kwargs) + except DownloadError: + # if an error occurs, try with the next index_url + if self._mirrors_tries >= self._mirrors_max_tries: + try: + self._switch_to_next_mirror() + except KeyError: + raise UnableToDownload("Tried all mirrors") + else: + self._mirrors_tries += 1 + self._projects.clear() + return wrapped(self, *args, **kwargs) + return wrapped + return wrapper + + +class Crawler(BaseClient): + """Provides useful tools to request the Python Package Index simple API. + + You can specify both mirrors and mirrors_url, but mirrors_url will only be + used if mirrors is set to None. :param index_url: the url of the simple index to search on. + :param prefer_final: if the version is not mentioned, and the last + version is not a "final" one (alpha, beta, etc.), + pick up the last final version. + :param prefer_source: if the distribution type is not mentioned, pick up + the source one if available. :param follow_externals: tell if following external links is needed or not. Default is False. :param hosts: a list of hosts allowed to be processed while using @@ -69,38 +101,33 @@ hosts. :param follow_externals: tell if following external links is needed or not. Default is False. - :param prefer_source: if there is binary and source distributions, the - source prevails. - :param prefer_final: if the version is not mentioned, and the last - version is not a "final" one (alpha, beta, etc.), - pick up the last final version. :param mirrors_url: the url to look on for DNS records giving mirror adresses. - :param mirrors: a list of mirrors to check out if problems - occurs while working with the one given in "url" + :param mirrors: a list of mirrors (see PEP 381). :param timeout: time in seconds to consider a url has timeouted. + :param mirrors_max_tries": number of times to try requesting informations + on mirrors before switching. """ - def __init__(self, index_url=PYPI_DEFAULT_INDEX_URL, hosts=DEFAULT_HOSTS, - follow_externals=False, prefer_source=True, - prefer_final=False, mirrors_url=PYPI_DEFAULT_MIRROR_URL, - mirrors=None, timeout=SOCKET_TIMEOUT): + def __init__(self, index_url=DEFAULT_SIMPLE_INDEX_URL, prefer_final=False, + prefer_source=True, hosts=DEFAULT_HOSTS, + follow_externals=False, mirrors_url=None, mirrors=None, + timeout=SOCKET_TIMEOUT, mirrors_max_tries=0): + super(Crawler, self).__init__(prefer_final, prefer_source) self.follow_externals = follow_externals + # mirroring attributes. if not index_url.endswith("/"): index_url += "/" - self._index_urls = [index_url] # if no mirrors are defined, use the method described in PEP 381. if mirrors is None: - try: - mirrors = socket.gethostbyname_ex(mirrors_url)[-1] - except socket.gaierror: - mirrors = [] - self._index_urls.extend(mirrors) - self._current_index_url = 0 + mirrors = get_mirrors(mirrors_url) + self._mirrors = set(mirrors) + self._mirrors_used = set() + self.index_url = index_url + self._mirrors_max_tries = mirrors_max_tries + self._mirrors_tries = 0 self._timeout = timeout - self._prefer_source = prefer_source - self._prefer_final = prefer_final # create a regexp to match all given hosts self._allowed_hosts = re.compile('|'.join(map(translate, hosts))).match @@ -109,96 +136,84 @@ # scanning them multple time (eg. if there is multiple pages pointing # on one) self._processed_urls = [] - self._distributions = {} + self._projects = {} - def find(self, requirements, prefer_source=None, prefer_final=None): - """Browse the PyPI to find distributions that fullfil the given - requirements. + @with_mirror_support() + def search_projects(self, name=None, **kwargs): + """Search the index for projects containing the given name. - :param requirements: A project name and it's distribution, using - version specifiers, as described in PEP345. - :type requirements: You can pass either a version.VersionPredicate - or a string. - :param prefer_source: if there is binary and source distributions, the - source prevails. - :param prefer_final: if the version is not mentioned, and the last - version is not a "final" one (alpha, beta, etc.), - pick up the last final version. + Return a list of names. """ - requirements = self._get_version_predicate(requirements) - if prefer_source is None: - prefer_source = self._prefer_source - if prefer_final is None: - prefer_final = self._prefer_final + index = self._open_url(self.index_url) + projectname = re.compile("""]*>(.?[^<]*%s.?[^<]*)""" % name, + flags=re.I) + matching_projects = [] + for match in projectname.finditer(index.read()): + project_name = match.group(1) + matching_projects.append(self._get_project(project_name)) + return matching_projects - # process the index for this project - self._process_pypi_page(requirements.name) - - # filter with requirements and return the results - if requirements.name in self._distributions: - dists = self._distributions[requirements.name].filter(requirements) - dists.sort_distributions(prefer_source=prefer_source, - prefer_final=prefer_final) - else: - dists = [] - - return dists - - def get(self, requirements, *args, **kwargs): - """Browse the PyPI index to find distributions that fullfil the - given requirements, and return the most recent one. - - You can specify prefer_final and prefer_source arguments here. - If not, the default one will be used. + def get_releases(self, requirements, prefer_final=None, + force_update=False): + """Search for releases and return a ReleaseList object containing + the results. """ predicate = self._get_version_predicate(requirements) - dists = self.find(predicate, *args, **kwargs) + if self._projects.has_key(predicate.name.lower()) and not force_update: + return self._projects.get(predicate.name.lower()) + prefer_final = self._get_prefer_final(prefer_final) + self._process_index_page(predicate.name) - if len(dists) == 0: - raise DistributionNotFound(requirements) + if not self._projects.has_key(predicate.name.lower()): + raise ProjectNotFound() - return dists.get_last(predicate) + releases = self._projects.get(predicate.name.lower()) + releases.sort_releases(prefer_final=prefer_final) + return releases - def download(self, requirements, temp_path=None, *args, **kwargs): - """Download the distribution, using the requirements. + def get_release(self, requirements, prefer_final=None): + """Return only one release that fulfill the given requirements""" + predicate = self._get_version_predicate(requirements) + release = self.get_releases(predicate, prefer_final)\ + .get_last(predicate) + if not release: + raise ReleaseNotFound("No release matches the given criterias") + return release - If more than one distribution match the requirements, use the last - version. - Download the distribution, and put it in the temp_path. If no temp_path - is given, creates and return one. + def get_distributions(self, project_name, version): + """Return the distributions found on the index for the specific given + release""" + # as the default behavior of get_release is to return a release + # containing the distributions, just alias it. + return self.get_release("%s (%s)" % (project_name, version)) - Returns the complete absolute path to the downloaded archive. + def get_metadata(self, project_name, version): + """Return the metadatas from the simple index. - :param requirements: The same as the find attribute of `find`. - - You can specify prefer_final and prefer_source arguments here. - If not, the default one will be used. + Currently, download one archive, extract it and use the PKG-INFO file. """ - return self.get(requirements, *args, **kwargs)\ - .download(path=temp_path) - - def _get_version_predicate(self, requirements): - """Return a VersionPredicate object, from a string or an already - existing object. - """ - if isinstance(requirements, str): - requirements = VersionPredicate(requirements) - return requirements - - @property - def index_url(self): - return self._index_urls[self._current_index_url] + release = self.get_distributions(project_name, version) + if not release._metadata: + location = release.get_distribution().unpack() + pkg_info = os.path.join(location, 'PKG-INFO') + release._metadata = DistributionMetadata(pkg_info) + return release def _switch_to_next_mirror(self): """Switch to the next mirror (eg. point self.index_url to the next - url. + mirror url. + + Raise a KeyError if all mirrors have been tried. """ - # Internally, iter over the _index_url iterable, if we have read all - # of the available indexes, raise an exception. - if self._current_index_url < len(self._index_urls): - self._current_index_url = self._current_index_url + 1 - else: - raise UnableToDownload("All mirrors fails") + self._mirrors_used.add(self.index_url) + index_url = self._mirrors.pop() + if not ("http://" or "https://" or "file://") in index_url: + index_url = "http://%s" % index_url + + if not index_url.endswith("/simple"): + index_url = "%s/simple/" % index_url + + self.index_url = index_url def _is_browsable(self, url): """Tell if the given URL can be browsed or not. @@ -228,18 +243,34 @@ return True return False - def _register_dist(self, dist): - """Register a distribution as a part of fetched distributions for - SimpleIndex. + def _register_release(self, release=None, release_info={}): + """Register a new release. - Return the PyPIDistributions object for the specified project name + Both a release or a dict of release_info can be provided, the prefered + way (eg. the quicker) is the dict one. + + Return the list of existing releases for the given project. """ - # Internally, check if a entry exists with the project name, if not, - # create a new one, and if exists, add the dist to the pool. - if not dist.name in self._distributions: - self._distributions[dist.name] = PyPIDistributions() - self._distributions[dist.name].append(dist) - return self._distributions[dist.name] + # Check if the project already has a list of releases (refering to + # the project name). If not, create a new release list. + # Then, add the release to the list. + if release: + name = release.name + else: + name = release_info['name'] + if not name.lower() in self._projects: + self._projects[name.lower()] = ReleasesList(name, + index=self._index) + + if release: + self._projects[name.lower()].add_release(release=release) + else: + name = release_info.pop('name') + version = release_info.pop('version') + dist_type = release_info.pop('dist_type') + self._projects[name.lower()].add_release(version, dist_type, + **release_info) + return self._projects[name.lower()] def _process_url(self, url, project_name=None, follow_links=True): """Process an url and search for distributions packages. @@ -264,9 +295,14 @@ if self._is_distribution(link) or is_download: self._processed_urls.append(link) # it's a distribution, so create a dist object - dist = PyPIDistribution.from_url(link, project_name, - is_external=not self.index_url in url) - self._register_dist(dist) + try: + infos = get_infos_from_url(link, project_name, + is_external=not self.index_url in url) + except CantParseArchiveName, e: + logging.warning("version has not been parsed: %s" + % e) + else: + self._register_release(release_info=infos) else: if self._is_browsable(link) and follow_links: self._process_url(link, project_name, @@ -280,6 +316,9 @@ else: return self._default_link_matcher + def _get_full_url(self, url, base_url): + return urlparse.urljoin(base_url, self._htmldecode(url)) + def _simple_link_matcher(self, content, base_url): """Yield all links with a rel="download" or rel="homepage". @@ -287,41 +326,39 @@ If follow_externals is set to False, dont yeld the external urls. """ + for match in HREF.finditer(content): + url = self._get_full_url(match.group(1), base_url) + if MD5_HASH.match(url): + yield (url, True) + for match in REL.finditer(content): + # search for rel links. tag, rel = match.groups() rels = map(str.strip, rel.lower().split(',')) if 'homepage' in rels or 'download' in rels: for match in HREF.finditer(tag): - url = urlparse.urljoin(base_url, - self._htmldecode(match.group(1))) + url = self._get_full_url(match.group(1), base_url) if 'download' in rels or self._is_browsable(url): # yield a list of (url, is_download) - yield (urlparse.urljoin(base_url, url), - 'download' in rels) + yield (url, 'download' in rels) def _default_link_matcher(self, content, base_url): """Yield all links found on the page. """ for match in HREF.finditer(content): - url = urlparse.urljoin(base_url, self._htmldecode(match.group(1))) + url = self._get_full_url(match.group(1), base_url) if self._is_browsable(url): yield (url, False) - def _process_pypi_page(self, name): + @with_mirror_support() + def _process_index_page(self, name): """Find and process a PyPI page for the given project name. :param name: the name of the project to find the page """ - try: - # Browse and index the content of the given PyPI page. - url = self.index_url + name + "/" - self._process_url(url, name) - except DownloadError: - # if an error occurs, try with the next index_url - # (provided by the mirrors) - self._switch_to_next_mirror() - self._distributions.clear() - self._process_pypi_page(name) + # Browse and index the content of the given PyPI page. + url = self.index_url + name + "/" + self._process_url(url, name) @socket_timeout() def _open_url(self, url): @@ -329,44 +366,35 @@ files support. """ + scheme, netloc, path, params, query, frag = urlparse.urlparse(url) + + # authentication stuff + if scheme in ('http', 'https'): + auth, host = urllib2.splituser(netloc) + else: + auth = None + + # add index.html automatically for filesystem paths + if scheme == 'file': + if url.endswith('/'): + url += "index.html" + + # add authorization headers if auth is provided + if auth: + auth = "Basic " + \ + urllib2.unquote(auth).encode('base64').strip() + new_url = urlparse.urlunparse(( + scheme, host, path, params, query, frag)) + request = urllib2.Request(new_url) + request.add_header("Authorization", auth) + else: + request = urllib2.Request(url) + request.add_header('User-Agent', USER_AGENT) try: - scheme, netloc, path, params, query, frag = urlparse.urlparse(url) - - if scheme in ('http', 'https'): - auth, host = urllib2.splituser(netloc) - else: - auth = None - - # add index.html automatically for filesystem paths - if scheme == 'file': - if url.endswith('/'): - url += "index.html" - - if auth: - auth = "Basic " + \ - urllib2.unquote(auth).encode('base64').strip() - new_url = urlparse.urlunparse(( - scheme, host, path, params, query, frag)) - request = urllib2.Request(new_url) - request.add_header("Authorization", auth) - else: - request = urllib2.Request(url) - request.add_header('User-Agent', USER_AGENT) fp = urllib2.urlopen(request) - - if auth: - # Put authentication info back into request URL if same host, - # so that links found on the page will work - s2, h2, path2, param2, query2, frag2 = \ - urlparse.urlparse(fp.url) - if s2 == scheme and h2 == host: - fp.url = urlparse.urlunparse( - (s2, netloc, path2, param2, query2, frag2)) - - return fp except (ValueError, httplib.InvalidURL), v: msg = ' '.join([str(arg) for arg in v.args]) - raise PyPIError('%s %s' % (url, msg)) + raise IndexesError('%s %s' % (url, msg)) except urllib2.HTTPError, v: return v except urllib2.URLError, v: @@ -376,6 +404,18 @@ 'The server might be down, %s' % (url, v.line)) except httplib.HTTPException, v: raise DownloadError("Download error for %s: %s" % (url, v)) + except socket.timeout: + raise DownloadError("The server timeouted") + + if auth: + # Put authentication info back into request URL if same host, + # so that links found on the page will work + s2, h2, path2, param2, query2, frag2 = \ + urlparse.urlparse(fp.url) + if s2 == scheme and h2 == host: + fp.url = urlparse.urlunparse( + (s2, netloc, path2, param2, query2, frag2)) + return fp def _decode_entity(self, match): what = match.group(1) diff --git a/src/distutils2/index/wrapper.py b/src/distutils2/index/wrapper.py new file mode 100644 --- /dev/null +++ b/src/distutils2/index/wrapper.py @@ -0,0 +1,93 @@ +import xmlrpc +import simple + +_WRAPPER_MAPPINGS = {'get_release': 'simple', + 'get_releases': 'simple', + 'search_projects': 'simple', + 'get_metadata': 'xmlrpc', + 'get_distributions': 'simple'} + +_WRAPPER_INDEXES = {'xmlrpc': xmlrpc.Client, + 'simple': simple.Crawler} + +def switch_index_if_fails(func, wrapper): + """Decorator that switch of index (for instance from xmlrpc to simple) + if the first mirror return an empty list or raises an exception. + """ + def decorator(*args, **kwargs): + retry = True + exception = None + methods = [func] + for f in wrapper._indexes.values(): + if f != func.im_self and hasattr(f, func.__name__): + methods.append(getattr(f, func.__name__)) + for method in methods: + try: + response = method(*args, **kwargs) + retry = False + except Exception, e: + exception = e + if not retry: + break + if retry and exception: + raise exception + else: + return response + return decorator + + +class ClientWrapper(object): + """Wrapper around simple and xmlrpc clients, + + Choose the best implementation to use depending the needs, using the given + mappings. + If one of the indexes returns an error, tries to use others indexes. + + :param index: tell wich index to rely on by default. + :param index_classes: a dict of name:class to use as indexes. + :param indexes: a dict of name:index already instantiated + :param mappings: the mappings to use for this wrapper + """ + + def __init__(self, default_index='simple', index_classes=_WRAPPER_INDEXES, + indexes={}, mappings=_WRAPPER_MAPPINGS): + self._projects = {} + self._mappings = mappings + self._indexes = indexes + self._default_index = default_index + + # instantiate the classes and set their _project attribute to the one + # of the wrapper. + for name, cls in index_classes.items(): + obj = self._indexes.setdefault(name, cls()) + obj._projects = self._projects + obj._index = self + + def __getattr__(self, method_name): + """When asking for methods of the wrapper, return the implementation of + the wrapped classes, depending the mapping. + + Decorate the methods to switch of implementation if an error occurs + """ + real_method = None + if method_name in _WRAPPER_MAPPINGS: + obj = self._indexes[_WRAPPER_MAPPINGS[method_name]] + real_method = getattr(obj, method_name) + else: + # the method is not defined in the mappings, so we try first to get + # it via the default index, and rely on others if needed. + try: + real_method = getattr(self._indexes[self._default_index], + method_name) + except AttributeError: + other_indexes = [i for i in self._indexes + if i != self._default_index] + for index in other_indexes: + real_method = getattr(self._indexes[index], method_name, None) + if real_method: + break + if real_method: + return switch_index_if_fails(real_method, self) + else: + raise AttributeError("No index have attribute '%s'" % method_name) + diff --git a/src/distutils2/index/xmlrpc.py b/src/distutils2/index/xmlrpc.py new file mode 100644 --- /dev/null +++ b/src/distutils2/index/xmlrpc.py @@ -0,0 +1,175 @@ +import logging +import xmlrpclib + +from distutils2.errors import IrrationalVersionError +from distutils2.index.base import BaseClient +from distutils2.index.errors import ProjectNotFound, InvalidSearchField +from distutils2.index.dist import ReleaseInfo + +__all__ = ['Client', 'DEFAULT_XMLRPC_INDEX_URL'] + +DEFAULT_XMLRPC_INDEX_URL = 'http://python.org/pypi' + +_SEARCH_FIELDS = ['name', 'version', 'author', 'author_email', 'maintainer', + 'maintainer_email', 'home_page', 'license', 'summary', + 'description', 'keywords', 'platform', 'download_url'] + + +class Client(BaseClient): + """Client to query indexes using XML-RPC method calls. + + If no server_url is specified, use the default PyPI XML-RPC URL, + defined in the DEFAULT_XMLRPC_INDEX_URL constant:: + + >>> client = XMLRPCClient() + >>> client.server_url == DEFAULT_XMLRPC_INDEX_URL + True + + >>> client = XMLRPCClient("http://someurl/") + >>> client.server_url + 'http://someurl/' + """ + + def __init__(self, server_url=DEFAULT_XMLRPC_INDEX_URL, prefer_final=False, + prefer_source=True): + super(Client, self).__init__(prefer_final, prefer_source) + self.server_url = server_url + self._projects = {} + + def get_release(self, requirements, prefer_final=False): + """Return a release with all complete metadata and distribution + related informations. + """ + prefer_final = self._get_prefer_final(prefer_final) + predicate = self._get_version_predicate(requirements) + releases = self.get_releases(predicate.name) + release = releases.get_last(predicate, prefer_final) + self.get_metadata(release.name, "%s" % release.version) + self.get_distributions(release.name, "%s" % release.version) + return release + + def get_releases(self, requirements, prefer_final=None, show_hidden=True, + force_update=False): + """Return the list of existing releases for a specific project. + + Cache the results from one call to another. + + If show_hidden is True, return the hidden releases too. + If force_update is True, reprocess the index to update the + informations (eg. make a new XML-RPC call). + :: + + >>> client = XMLRPCClient() + >>> client.get_releases('Foo') + ['1.1', '1.2', '1.3'] + + If no such project exists, raise a ProjectNotFound exception:: + + >>> client.get_project_versions('UnexistingProject') + ProjectNotFound: UnexistingProject + + """ + def get_versions(project_name, show_hidden): + return self.proxy.package_releases(project_name, show_hidden) + + predicate = self._get_version_predicate(requirements) + prefer_final = self._get_prefer_final(prefer_final) + project_name = predicate.name + if not force_update and (project_name.lower() in self._projects): + project = self._projects[project_name.lower()] + if not project.contains_hidden and show_hidden: + # if hidden releases are requested, and have an existing + # list of releases that does not contains hidden ones + all_versions = get_versions(project_name, show_hidden) + existing_versions = project.get_versions() + hidden_versions = list(set(all_versions) - + set(existing_versions)) + for version in hidden_versions: + project.add_release(release=ReleaseInfo(project_name, + version, index=self._index)) + else: + versions = get_versions(project_name, show_hidden) + if not versions: + raise ProjectNotFound(project_name) + project = self._get_project(project_name) + project.add_releases([ReleaseInfo(project_name, version, + index=self._index) + for version in versions]) + project = project.filter(predicate) + project.sort_releases(prefer_final) + return project + + + def get_distributions(self, project_name, version): + """Grab informations about distributions from XML-RPC. + + Return a ReleaseInfo object, with distribution-related informations + filled in. + """ + url_infos = self.proxy.release_urls(project_name, version) + project = self._get_project(project_name) + if version not in project.get_versions(): + project.add_release(release=ReleaseInfo(project_name, version, + index=self._index)) + release = project.get_release(version) + for info in url_infos: + packagetype = info['packagetype'] + dist_infos = {'url': info['url'], + 'hashval': info['md5_digest'], + 'hashname': 'md5', + 'is_external': False, + 'python_version': info['python_version']} + release.add_distribution(packagetype, **dist_infos) + return release + + def get_metadata(self, project_name, version): + """Retreive project metadatas. + + Return a ReleaseInfo object, with metadata informations filled in. + """ + metadata = self.proxy.release_data(project_name, version) + project = self._get_project(project_name) + if version not in project.get_versions(): + project.add_release(release=ReleaseInfo(project_name, version, + index=self._index)) + release = project.get_release(version) + release.set_metadata(metadata) + return release + + def search_projects(self, name=None, operator="or", **kwargs): + """Find using the keys provided in kwargs. + + You can set operator to "and" or "or". + """ + for key in kwargs: + if key not in _SEARCH_FIELDS: + raise InvalidSearchField(key) + if name: + kwargs["name"] = name + projects = self.proxy.search(kwargs, operator) + for p in projects: + project = self._get_project(p['name']) + try: + project.add_release(release=ReleaseInfo(p['name'], + p['version'], metadata={'summary': p['summary']}, + index=self._index)) + except IrrationalVersionError, e: + logging.warn("Irrational version error found: %s" % e) + + return [self._projects[p['name'].lower()] for p in projects] + + @property + def proxy(self): + """Property used to return the XMLRPC server proxy. + + If no server proxy is defined yet, creates a new one:: + + >>> client = XmlRpcClient() + >>> client.proxy() + + + """ + if not hasattr(self, '_server_proxy'): + self._server_proxy = xmlrpclib.ServerProxy(self.server_url) + + return self._server_proxy diff --git a/src/distutils2/metadata.py b/src/distutils2/metadata.py --- a/src/distutils2/metadata.py +++ b/src/distutils2/metadata.py @@ -183,9 +183,12 @@ """The metadata of a release. Supports versions 1.0, 1.1 and 1.2 (auto-detected). + + if from_dict attribute is set, all key/values pairs will be sent to the + "set" method, building the metadata from the dict. """ def __init__(self, path=None, platform_dependent=False, - execution_context=None, fileobj=None): + execution_context=None, fileobj=None, mapping=None): self._fields = {} self.version = None self.docutils_support = _HAS_DOCUTILS @@ -195,6 +198,8 @@ elif fileobj is not None: self.read_file(fileobj) self.execution_context = execution_context + if mapping: + self.update(mapping) def _set_best_version(self): self.version = _best_version(self._fields) @@ -322,6 +327,38 @@ for value in values: self._write_field(fileobject, field, value) + def update(self, other=None, **kwargs): + """Set metadata values from the given mapping + + Convert the keys to Metadata fields. Given keys that don't match a + metadata argument will not be used. + + If overwrite is set to False, just add metadata values that are + actually not defined. + + If there is existing values in conflict with the dictionary ones, the + new values prevails. + + Empty values (e.g. None and []) are not setted this way. + """ + def _set(key, value): + if value not in ([], None) and key in _ATTR2FIELD: + self.set(self._convert_name(key), value) + + if other is None: + pass + elif hasattr(other, 'iteritems'): # iteritems saves memory and lookups + for k, v in other.iteritems(): + _set(k, v) + elif hasattr(other, 'keys'): + for k in other.keys(): + _set(k, v) + else: + for k, v in other: + _set(k, v) + if kwargs: + self.update(kwargs) + def set(self, name, value): """Control then set a metadata field.""" name = self._convert_name(name) diff --git a/src/distutils2/tests/pypi_server.py b/src/distutils2/tests/pypi_server.py --- a/src/distutils2/tests/pypi_server.py +++ b/src/distutils2/tests/pypi_server.py @@ -5,17 +5,28 @@ before any use. """ +import os import Queue +import SocketServer +import select import threading + from BaseHTTPServer import HTTPServer from SimpleHTTPServer import SimpleHTTPRequestHandler -import os.path -import select +from SimpleXMLRPCServer import SimpleXMLRPCServer from distutils2.tests.support import unittest PYPI_DEFAULT_STATIC_PATH = os.path.dirname(os.path.abspath(__file__)) + "/pypiserver" +def use_xmlrpc_server(*server_args, **server_kwargs): + server_kwargs['serve_xmlrpc'] = True + return use_pypi_server(*server_args, **server_kwargs) + +def use_http_server(*server_args, **server_kwargs): + server_kwargs['serve_xmlrpc'] = False + return use_pypi_server(*server_args, **server_kwargs) + def use_pypi_server(*server_args, **server_kwargs): """Decorator to make use of the PyPIServer for test methods, just when needed, and not for the entire duration of the testcase. @@ -50,38 +61,58 @@ """ def __init__(self, test_static_path=None, - static_filesystem_paths=["default"], static_uri_paths=["simple"]): + static_filesystem_paths=["default"], + static_uri_paths=["simple"], serve_xmlrpc=False) : """Initialize the server. + + Default behavior is to start the HTTP server. You can either start the + xmlrpc server by setting xmlrpc to True. Caution: Only one server will + be started. static_uri_paths and static_base_path are parameters used to provides respectively the http_paths to serve statically, and where to find the matching files on the filesystem. """ + # we want to launch the server in a new dedicated thread, to not freeze + # tests. threading.Thread.__init__(self) self._run = True - self.httpd = HTTPServer(('', 0), PyPIRequestHandler) - self.httpd.RequestHandlerClass.log_request = lambda *_: None - self.httpd.RequestHandlerClass.pypi_server = self - self.address = (self.httpd.server_name, self.httpd.server_port) - self.request_queue = Queue.Queue() - self._requests = [] - self.default_response_status = 200 - self.default_response_headers = [('Content-type', 'text/plain')] - self.default_response_data = "hello" - - # initialize static paths / filesystems - self.static_uri_paths = static_uri_paths - if test_static_path is not None: - static_filesystem_paths.append(test_static_path) - self.static_filesystem_paths = [PYPI_DEFAULT_STATIC_PATH + "/" + path - for path in static_filesystem_paths] + self._serve_xmlrpc = serve_xmlrpc + + if not self._serve_xmlrpc: + self.server = HTTPServer(('', 0), PyPIRequestHandler) + self.server.RequestHandlerClass.pypi_server = self + + self.request_queue = Queue.Queue() + self._requests = [] + self.default_response_status = 200 + self.default_response_headers = [('Content-type', 'text/plain')] + self.default_response_data = "hello" + + # initialize static paths / filesystems + self.static_uri_paths = static_uri_paths + if test_static_path is not None: + static_filesystem_paths.append(test_static_path) + self.static_filesystem_paths = [PYPI_DEFAULT_STATIC_PATH + "/" + path + for path in static_filesystem_paths] + else: + # xmlrpc server + self.server = PyPIXMLRPCServer(('', 0)) + self.xmlrpc = XMLRPCMockIndex() + # register the xmlrpc methods + self.server.register_introspection_functions() + self.server.register_instance(self.xmlrpc) + + self.address = (self.server.server_name, self.server.server_port) + # to not have unwanted outputs. + self.server.RequestHandlerClass.log_request = lambda *_: None def run(self): # loop because we can't stop it otherwise, for python < 2.6 while self._run: - r, w, e = select.select([self.httpd], [], [], 0.5) + r, w, e = select.select([self.server], [], [], 0.5) if r: - self.httpd.handle_request() + self.server.handle_request() def stop(self): """self shutdown is not supported for python < 2.6""" @@ -191,3 +222,180 @@ self.send_header(header, value) self.end_headers() self.wfile.write(data) + +class PyPIXMLRPCServer(SimpleXMLRPCServer): + def server_bind(self): + """Override server_bind to store the server name.""" + SocketServer.TCPServer.server_bind(self) + host, port = self.socket.getsockname()[:2] + self.server_name = socket.getfqdn(host) + self.server_port = port + +class MockDist(object): + """Fake distribution, used in the Mock PyPI Server""" + def __init__(self, name, version="1.0", hidden=False, url="http://url/", + type="sdist", filename="", size=10000, + digest="123456", downloads=7, has_sig=False, + python_version="source", comment="comment", + author="John Doe", author_email="john at doe.name", + maintainer="Main Tayner", maintainer_email="maintainer_mail", + project_url="http://project_url/", homepage="http://homepage/", + keywords="", platform="UNKNOWN", classifiers=[], licence="", + description="Description", summary="Summary", stable_version="", + ordering="", documentation_id="", code_kwalitee_id="", + installability_id="", obsoletes=[], obsoletes_dist=[], + provides=[], provides_dist=[], requires=[], requires_dist=[], + requires_external=[], requires_python=""): + + # basic fields + self.name = name + self.version = version + self.hidden = hidden + + # URL infos + self.url = url + self.digest = digest + self.downloads = downloads + self.has_sig = has_sig + self.python_version = python_version + self.comment = comment + self.type = type + + # metadata + self.author = author + self.author_email = author_email + self.maintainer = maintainer + self.maintainer_email = maintainer_email + self.project_url = project_url + self.homepage = homepage + self.keywords = keywords + self.platform = platform + self.classifiers = classifiers + self.licence = licence + self.description = description + self.summary = summary + self.stable_version = stable_version + self.ordering = ordering + self.cheesecake_documentation_id = documentation_id + self.cheesecake_code_kwalitee_id = code_kwalitee_id + self.cheesecake_installability_id = installability_id + + self.obsoletes = obsoletes + self.obsoletes_dist = obsoletes_dist + self.provides = provides + self.provides_dist = provides_dist + self.requires = requires + self.requires_dist = requires_dist + self.requires_external = requires_external + self.requires_python = requires_python + + def url_infos(self): + return { + 'url': self.url, + 'packagetype': self.type, + 'filename': 'filename.tar.gz', + 'size': '6000', + 'md5_digest': self.digest, + 'downloads': self.downloads, + 'has_sig': self.has_sig, + 'python_version': self.python_version, + 'comment_text': self.comment, + } + + def metadata(self): + return { + 'maintainer': self.maintainer, + 'project_url': [self.project_url], + 'maintainer_email': self.maintainer_email, + 'cheesecake_code_kwalitee_id': self.cheesecake_code_kwalitee_id, + 'keywords': self.keywords, + 'obsoletes_dist': self.obsoletes_dist, + 'requires_external': self.requires_external, + 'author': self.author, + 'author_email': self.author_email, + 'download_url': self.url, + 'platform': self.platform, + 'version': self.version, + 'obsoletes': self.obsoletes, + 'provides': self.provides, + 'cheesecake_documentation_id': self.cheesecake_documentation_id, + '_pypi_hidden': self.hidden, + 'description': self.description, + '_pypi_ordering': 19, + 'requires_dist': self.requires_dist, + 'requires_python': self.requires_python, + 'classifiers': [], + 'name': self.name, + 'licence': self.licence, + 'summary': self.summary, + 'home_page': self.homepage, + 'stable_version': self.stale_version, + 'provides_dist': self.provides_dist, + 'requires': self.requires, + 'cheesecake_installability_id': self.cheesecake_installability_id, + } + + def search_result(self): + return { + '_pypi_ordering': 0, + 'version': self.version, + 'name': self.name, + 'summary': self.summary, + } + +class XMLRPCMockIndex(object): + """Mock XMLRPC server""" + + def __init__(self, dists=[]): + self._dists = dists + + def add_distributions(self, dists): + for dist in dists: + self._dists.append(MockDist(**dist)) + + def set_distributions(self, dists): + self._dists = [] + self.add_distributions(dists) + + def set_search_result(self, result): + """set a predefined search result""" + self._search_result = result + + def _get_search_results(self): + results = [] + for name in self._search_result: + found_dist = [d for d in self._dists if d.name == name] + if found_dist: + results.append(found_dist[0]) + else: + dist = MockDist(name) + results.append(dist) + self._dists.append(dist) + return [r.search_result() for r in results] + + def list_package(self): + return [d.name for d in self._dists] + + def package_releases(self, package_name, show_hidden=False): + if show_hidden: + # return all + return [d.version for d in self._dists if d.name == package_name] + else: + # return only un-hidden + return [d.version for d in self._dists if d.name == package_name + and not d.hidden] + + def release_urls(self, package_name, version): + return [d.url_infos() for d in self._dists + if d.name == package_name and d.version == version] + + def release_data(self, package_name, version): + release = [d for d in self._dists + if d.name == package_name and d.version == version] + if release: + return release[0].metadata() + else: + return {} + + def search(self, spec, operator="and"): + return self._get_search_results() diff --git a/src/distutils2/tests/pypiserver/project_list/simple/index.html b/src/distutils2/tests/pypiserver/project_list/simple/index.html new file mode 100644 --- /dev/null +++ b/src/distutils2/tests/pypiserver/project_list/simple/index.html @@ -0,0 +1,5 @@ +FooBar-bar +Foobar-baz +Baz-FooBar +Baz +Foo diff --git a/src/distutils2/tests/test_pypi_dist.py b/src/distutils2/tests/test_index_dist.py rename from src/distutils2/tests/test_pypi_dist.py rename to src/distutils2/tests/test_index_dist.py --- a/src/distutils2/tests/test_pypi_dist.py +++ b/src/distutils2/tests/test_index_dist.py @@ -1,72 +1,93 @@ -"""Tests for the distutils2.pypi.dist module.""" +"""Tests for the distutils2.index.dist module.""" + +import os from distutils2.tests.pypi_server import use_pypi_server from distutils2.tests import run_unittest from distutils2.tests.support import unittest, TempdirManager from distutils2.version import VersionPredicate -from distutils2.pypi.errors import HashDoesNotMatch, UnsupportedHashName -from distutils2.pypi.dist import (PyPIDistribution as Dist, - PyPIDistributions as Dists, - split_archive_name) +from distutils2.index.errors import HashDoesNotMatch, UnsupportedHashName +from distutils2.index.dist import (ReleaseInfo, ReleasesList, DistInfo, + split_archive_name, get_infos_from_url) -class TestPyPIDistribution(TempdirManager, - unittest.TestCase): - """Tests the pypi.dist.PyPIDistribution class""" +def Dist(*args, **kwargs): + # DistInfo takes a release as a first parameter, avoid this in tests. + return DistInfo(None, *args, **kwargs) + + +class TestReleaseInfo(unittest.TestCase): def test_instantiation(self): - # Test the Distribution class provides us the good attributes when + # Test the DistInfo class provides us the good attributes when # given on construction - dist = Dist("FooBar", "1.1") - self.assertEqual("FooBar", dist.name) - self.assertEqual("1.1", "%s" % dist.version) + release = ReleaseInfo("FooBar", "1.1") + self.assertEqual("FooBar", release.name) + self.assertEqual("1.1", "%s" % release.version) - def test_create_from_url(self): - # Test that the Distribution object can be built from a single URL + def test_add_dist(self): + # empty distribution type should assume "sdist" + release = ReleaseInfo("FooBar", "1.1") + release.add_distribution(url="http://example.org/") + # should not fail + release['sdist'] + + def test_get_unknown_distribution(self): + # should raise a KeyError + pass + + def test_get_infos_from_url(self): + # Test that the the URLs are parsed the right way url_list = { 'FooBar-1.1.0.tar.gz': { 'name': 'foobar', # lowercase the name - 'version': '1.1', + 'version': '1.1.0', }, 'Foo-Bar-1.1.0.zip': { 'name': 'foo-bar', # keep the dash - 'version': '1.1', + 'version': '1.1.0', }, 'foobar-1.1b2.tar.gz#md5=123123123123123': { 'name': 'foobar', 'version': '1.1b2', - 'url': { - 'url': 'http://test.tld/foobar-1.1b2.tar.gz', # no hash - 'hashval': '123123123123123', - 'hashname': 'md5', - } + 'url': 'http://example.org/foobar-1.1b2.tar.gz', # no hash + 'hashval': '123123123123123', + 'hashname': 'md5', }, 'foobar-1.1-rc2.tar.gz': { # use suggested name 'name': 'foobar', 'version': '1.1c2', - 'url': { - 'url': 'http://test.tld/foobar-1.1-rc2.tar.gz', - } + 'url': 'http://example.org/foobar-1.1-rc2.tar.gz', } } for url, attributes in url_list.items(): - dist = Dist.from_url("http://test.tld/" + url) - for attribute, value in attributes.items(): - if isinstance(value, dict): - mylist = getattr(dist, attribute) - for val in value.keys(): - self.assertEqual(value[val], mylist[val]) + # for each url + infos = get_infos_from_url("http://example.org/" + url) + for attribute, expected in attributes.items(): + got = infos.get(attribute) + if attribute == "version": + self.assertEqual("%s" % got, expected) else: - if attribute == "version": - self.assertEqual(str(getattr(dist, "version")), value) - else: - self.assertEqual(getattr(dist, attribute), value) + self.assertEqual(got, expected) + + def test_split_archive_name(self): + # Test we can split the archive names + names = { + 'foo-bar-baz-1.0-rc2': ('foo-bar-baz', '1.0c2'), + 'foo-bar-baz-1.0': ('foo-bar-baz', '1.0'), + 'foobarbaz-1.0': ('foobarbaz', '1.0'), + } + for name, results in names.items(): + self.assertEqual(results, split_archive_name(name)) + + +class TestDistInfo(TempdirManager, unittest.TestCase): def test_get_url(self): # Test that the url property works well - d = Dist("foobar", "1.1", url="test_url") + d = Dist(url="test_url") self.assertDictEqual(d.url, { "url": "test_url", "is_external": True, @@ -83,13 +104,13 @@ "hashname": None, "hashval": None, }) - self.assertEqual(2, len(d._urls)) + self.assertEqual(2, len(d.urls)) - def test_comparaison(self): - # Test that we can compare PyPIDistributions - foo1 = Dist("foo", "1.0") - foo2 = Dist("foo", "2.0") - bar = Dist("bar", "2.0") + def test_comparison(self): + # Test that we can compare DistInfoributionInfoList + foo1 = ReleaseInfo("foo", "1.0") + foo2 = ReleaseInfo("foo", "2.0") + bar = ReleaseInfo("bar", "2.0") # assert we use the version to compare self.assertTrue(foo1 < foo2) self.assertFalse(foo1 > foo2) @@ -98,34 +119,23 @@ # assert we can't compare dists with different names self.assertRaises(TypeError, foo1.__eq__, bar) - def test_split_archive_name(self): - # Test we can split the archive names - names = { - 'foo-bar-baz-1.0-rc2': ('foo-bar-baz', '1.0c2'), - 'foo-bar-baz-1.0': ('foo-bar-baz', '1.0'), - 'foobarbaz-1.0': ('foobarbaz', '1.0'), - } - for name, results in names.items(): - self.assertEqual(results, split_archive_name(name)) - @use_pypi_server("downloads_with_md5") def test_download(self, server): # Download is possible, and the md5 is checked if given url = "%s/simple/foobar/foobar-0.1.tar.gz" % server.full_address # check md5 if given - dist = Dist("FooBar", "0.1", url=url, url_hashname="md5", - url_hashval="d41d8cd98f00b204e9800998ecf8427e") + dist = Dist(url=url, hashname="md5", + hashval="d41d8cd98f00b204e9800998ecf8427e") dist.download(self.mkdtemp()) # a wrong md5 fails - dist2 = Dist("FooBar", "0.1", url=url, - url_hashname="md5", url_hashval="wrongmd5") + dist2 = Dist(url=url, hashname="md5", hashval="wrongmd5") self.assertRaises(HashDoesNotMatch, dist2.download, self.mkdtemp()) # we can omit the md5 hash - dist3 = Dist("FooBar", "0.1", url=url) + dist3 = Dist(url=url) dist3.download(self.mkdtemp()) # and specify a temporary location @@ -134,106 +144,104 @@ dist3.download(path=path1) # and for a new one path2_base = self.mkdtemp() - dist4 = Dist("FooBar", "0.1", url=url) + dist4 = Dist(url=url) path2 = dist4.download(path=path2_base) self.assertTrue(path2_base in path2) def test_hashname(self): # Invalid hashnames raises an exception on assignation - Dist("FooBar", "0.1", url_hashname="md5", url_hashval="value") + Dist(hashname="md5", hashval="value") - self.assertRaises(UnsupportedHashName, Dist, "FooBar", "0.1", - url_hashname="invalid_hashname", url_hashval="value") + self.assertRaises(UnsupportedHashName, Dist, + hashname="invalid_hashname", + hashval="value") -class TestPyPIDistributions(unittest.TestCase): +class TestReleasesList(unittest.TestCase): def test_filter(self): # Test we filter the distributions the right way, using version # predicate match method - dists = Dists(( - Dist("FooBar", "1.1"), - Dist("FooBar", "1.1.1"), - Dist("FooBar", "1.2"), - Dist("FooBar", "1.2.1"), + releases = ReleasesList('FooBar', ( + ReleaseInfo("FooBar", "1.1"), + ReleaseInfo("FooBar", "1.1.1"), + ReleaseInfo("FooBar", "1.2"), + ReleaseInfo("FooBar", "1.2.1"), )) - filtered = dists.filter(VersionPredicate("FooBar (<1.2)")) - self.assertNotIn(dists[2], filtered) - self.assertNotIn(dists[3], filtered) - self.assertIn(dists[0], filtered) - self.assertIn(dists[1], filtered) + filtered = releases.filter(VersionPredicate("FooBar (<1.2)")) + self.assertNotIn(releases[2], filtered) + self.assertNotIn(releases[3], filtered) + self.assertIn(releases[0], filtered) + self.assertIn(releases[1], filtered) def test_append(self): # When adding a new item to the list, the behavior is to test if - # a distribution with the same name and version number already exists, - # and if so, to add url informations to the existing PyPIDistribution + # a release with the same name and version number already exists, + # and if so, to add a new distribution for it. If the distribution type + # is already defined too, add url informations to the existing DistInfo # object. - # If no object matches, just add "normally" the object to the list. - dists = Dists([ - Dist("FooBar", "1.1", url="external_url", type="source"), + releases = ReleasesList("FooBar", [ + ReleaseInfo("FooBar", "1.1", url="external_url", + dist_type="sdist"), ]) - self.assertEqual(1, len(dists)) - dists.append(Dist("FooBar", "1.1", url="internal_url", - url_is_external=False, type="source")) - self.assertEqual(1, len(dists)) - self.assertEqual(2, len(dists[0]._urls)) + self.assertEqual(1, len(releases)) + releases.add_release(release=ReleaseInfo("FooBar", "1.1", + url="internal_url", + is_external=False, + dist_type="sdist")) + self.assertEqual(1, len(releases)) + self.assertEqual(2, len(releases[0]['sdist'].urls)) - dists.append(Dist("Foobar", "1.1.1", type="source")) - self.assertEqual(2, len(dists)) + releases.add_release(release=ReleaseInfo("FooBar", "1.1.1", + dist_type="sdist")) + self.assertEqual(2, len(releases)) # when adding a distribution whith a different type, a new distribution # has to be added. - dists.append(Dist("Foobar", "1.1.1", type="binary")) - self.assertEqual(3, len(dists)) + releases.add_release(release=ReleaseInfo("FooBar", "1.1.1", + dist_type="bdist")) + self.assertEqual(2, len(releases)) + self.assertEqual(2, len(releases[1].dists)) def test_prefer_final(self): # Can order the distributions using prefer_final - fb10 = Dist("FooBar", "1.0") # final distribution - fb11a = Dist("FooBar", "1.1a1") # alpha - fb12a = Dist("FooBar", "1.2a1") # alpha - fb12b = Dist("FooBar", "1.2b1") # beta - dists = Dists([fb10, fb11a, fb12a, fb12b]) + fb10 = ReleaseInfo("FooBar", "1.0") # final distribution + fb11a = ReleaseInfo("FooBar", "1.1a1") # alpha + fb12a = ReleaseInfo("FooBar", "1.2a1") # alpha + fb12b = ReleaseInfo("FooBar", "1.2b1") # beta + dists = ReleasesList("FooBar", [fb10, fb11a, fb12a, fb12b]) - dists.sort_distributions(prefer_final=True) + dists.sort_releases(prefer_final=True) self.assertEqual(fb10, dists[0]) - dists.sort_distributions(prefer_final=False) + dists.sort_releases(prefer_final=False) self.assertEqual(fb12b, dists[0]) - def test_prefer_source(self): - # Ordering support prefer_source - fb_source = Dist("FooBar", "1.0", type="source") - fb_binary = Dist("FooBar", "1.0", type="binary") - fb2_binary = Dist("FooBar", "2.0", type="binary") - dists = Dists([fb_binary, fb_source]) - - dists.sort_distributions(prefer_source=True) - self.assertEqual(fb_source, dists[0]) - - dists.sort_distributions(prefer_source=False) - self.assertEqual(fb_binary, dists[0]) - - dists.append(fb2_binary) - dists.sort_distributions(prefer_source=True) - self.assertEqual(fb2_binary, dists[0]) - - def test_get_same_name_and_version(self): - # PyPIDistributions can return a list of "duplicates" - fb_source = Dist("FooBar", "1.0", type="source") - fb_binary = Dist("FooBar", "1.0", type="binary") - fb2_binary = Dist("FooBar", "2.0", type="binary") - dists = Dists([fb_binary, fb_source, fb2_binary]) - duplicates = dists.get_same_name_and_version() - self.assertTrue(1, len(duplicates)) - self.assertIn(fb_source, duplicates[0]) +# def test_prefer_source(self): +# # Ordering support prefer_source +# fb_source = Dist("FooBar", "1.0", type="source") +# fb_binary = Dist("FooBar", "1.0", type="binary") +# fb2_binary = Dist("FooBar", "2.0", type="binary") +# dists = ReleasesList([fb_binary, fb_source]) +# +# dists.sort_distributions(prefer_source=True) +# self.assertEqual(fb_source, dists[0]) +# +# dists.sort_distributions(prefer_source=False) +# self.assertEqual(fb_binary, dists[0]) +# +# dists.append(fb2_binary) +# dists.sort_distributions(prefer_source=True) +# self.assertEqual(fb2_binary, dists[0]) def test_suite(): suite = unittest.TestSuite() - suite.addTest(unittest.makeSuite(TestPyPIDistribution)) - suite.addTest(unittest.makeSuite(TestPyPIDistributions)) + suite.addTest(unittest.makeSuite(TestDistInfo)) + suite.addTest(unittest.makeSuite(TestReleaseInfo)) + suite.addTest(unittest.makeSuite(TestReleasesList)) return suite if __name__ == '__main__': diff --git a/src/distutils2/tests/test_pypi_simple.py b/src/distutils2/tests/test_index_simple.py rename from src/distutils2/tests/test_pypi_simple.py rename to src/distutils2/tests/test_index_simple.py --- a/src/distutils2/tests/test_pypi_simple.py +++ b/src/distutils2/tests/test_index_simple.py @@ -3,35 +3,34 @@ """ import sys import os -import shutil import urllib2 -from distutils2.pypi import simple -from distutils2.tests import support, run_unittest +from distutils2.index.simple import Crawler +from distutils2.tests import support from distutils2.tests.support import unittest from distutils2.tests.pypi_server import (use_pypi_server, PyPIServer, PYPI_DEFAULT_STATIC_PATH) -class PyPISimpleTestCase(support.TempdirManager, - unittest.TestCase): +class SimpleCrawlerTestCase(support.TempdirManager, unittest.TestCase): - def _get_simple_index(self, server, base_url="/simple/", hosts=None, + def _get_simple_crawler(self, server, base_url="/simple/", hosts=None, *args, **kwargs): - """Build and return a SimpleSimpleIndex instance, with the test server + """Build and return a SimpleIndex instance, with the test server urls """ if hosts is None: hosts = (server.full_address.strip("http://"),) kwargs['hosts'] = hosts - return simple.SimpleIndex(server.full_address + base_url, *args, + return Crawler(server.full_address + base_url, *args, **kwargs) - def test_bad_urls(self): - index = simple.SimpleIndex() + @use_pypi_server() + def test_bad_urls(self, server): + crawler = Crawler() url = 'http://127.0.0.1:0/nonesuch/test_simple' try: - v = index._open_url(url) + v = crawler._open_url(url) except Exception, v: self.assertTrue(url in str(v)) else: @@ -40,10 +39,10 @@ # issue 16 # easy_install inquant.contentmirror.plone breaks because of a typo # in its home URL - index = simple.SimpleIndex(hosts=('www.example.com',)) + crawler = Crawler(hosts=('example.org',)) url = 'url:%20https://svn.plone.org/svn/collective/inquant.contentmirror.plone/trunk' try: - v = index._open_url(url) + v = crawler._open_url(url) except Exception, v: self.assertTrue(url in str(v)) else: @@ -55,10 +54,10 @@ old_urlopen = urllib2.urlopen urllib2.urlopen = _urlopen - url = 'http://example.com' + url = 'http://example.org' try: try: - v = index._open_url(url) + v = crawler._open_url(url) except Exception, v: self.assertTrue('line' in str(v)) else: @@ -69,91 +68,91 @@ # issue 20 url = 'http://http://svn.pythonpaste.org/Paste/wphp/trunk' try: - index._open_url(url) + crawler._open_url(url) except Exception, v: self.assertTrue('nonnumeric port' in str(v)) # issue #160 if sys.version_info[0] == 2 and sys.version_info[1] == 7: # this should not fail - url = 'http://example.com' + url = server.full_address page = ('') - index._process_url(url, page) + crawler._process_url(url, page) @use_pypi_server("test_found_links") def test_found_links(self, server): - # Browse the index, asking for a specified distribution version + # Browse the index, asking for a specified release version # The PyPI index contains links for version 1.0, 1.1, 2.0 and 2.0.1 - index = self._get_simple_index(server) - last_distribution = index.get("foobar") + crawler = self._get_simple_crawler(server) + last_release = crawler.get_release("foobar") # we have scanned the index page self.assertIn(server.full_address + "/simple/foobar/", - index._processed_urls) + crawler._processed_urls) - # we have found 4 distributions in this page - self.assertEqual(len(index._distributions["foobar"]), 4) + # we have found 4 releases in this page + self.assertEqual(len(crawler._projects["foobar"]), 4) # and returned the most recent one - self.assertEqual("%s" % last_distribution.version, '2.0.1') + self.assertEqual("%s" % last_release.version, '2.0.1') def test_is_browsable(self): - index = simple.SimpleIndex(follow_externals=False) - self.assertTrue(index._is_browsable(index.index_url + "test")) + crawler = Crawler(follow_externals=False) + self.assertTrue(crawler._is_browsable(crawler.index_url + "test")) # Now, when following externals, we can have a list of hosts to trust. # and don't follow other external links than the one described here. - index = simple.SimpleIndex(hosts=["pypi.python.org", "test.org"], + crawler = Crawler(hosts=["pypi.python.org", "example.org"], follow_externals=True) good_urls = ( "http://pypi.python.org/foo/bar", "http://pypi.python.org/simple/foobar", - "http://test.org", - "http://test.org/", - "http://test.org/simple/", + "http://example.org", + "http://example.org/", + "http://example.org/simple/", ) bad_urls = ( "http://python.org", - "http://test.tld", + "http://example.tld", ) for url in good_urls: - self.assertTrue(index._is_browsable(url)) + self.assertTrue(crawler._is_browsable(url)) for url in bad_urls: - self.assertFalse(index._is_browsable(url)) + self.assertFalse(crawler._is_browsable(url)) # allow all hosts - index = simple.SimpleIndex(follow_externals=True, hosts=("*",)) - self.assertTrue(index._is_browsable("http://an-external.link/path")) - self.assertTrue(index._is_browsable("pypi.test.tld/a/path")) + crawler = Crawler(follow_externals=True, hosts=("*",)) + self.assertTrue(crawler._is_browsable("http://an-external.link/path")) + self.assertTrue(crawler._is_browsable("pypi.example.org/a/path")) # specify a list of hosts we want to allow - index = simple.SimpleIndex(follow_externals=True, - hosts=("*.test.tld",)) - self.assertFalse(index._is_browsable("http://an-external.link/path")) - self.assertTrue(index._is_browsable("http://pypi.test.tld/a/path")) + crawler = Crawler(follow_externals=True, + hosts=("*.example.org",)) + self.assertFalse(crawler._is_browsable("http://an-external.link/path")) + self.assertTrue(crawler._is_browsable("http://pypi.example.org/a/path")) @use_pypi_server("with_externals") - def test_restrict_hosts(self, server): + def test_follow_externals(self, server): # Include external pages # Try to request the package index, wich contains links to "externals" # resources. They have to be scanned too. - index = self._get_simple_index(server, follow_externals=True) - index.get("foobar") + crawler = self._get_simple_crawler(server, follow_externals=True) + crawler.get_release("foobar") self.assertIn(server.full_address + "/external/external.html", - index._processed_urls) + crawler._processed_urls) @use_pypi_server("with_real_externals") def test_restrict_hosts(self, server): # Only use a list of allowed hosts is possible # Test that telling the simple pyPI client to not retrieve external # works - index = self._get_simple_index(server, follow_externals=False) - index.get("foobar") + crawler = self._get_simple_crawler(server, follow_externals=False) + crawler.get_release("foobar") self.assertNotIn(server.full_address + "/external/external.html", - index._processed_urls) + crawler._processed_urls) @use_pypi_server(static_filesystem_paths=["with_externals"], static_uri_paths=["simple", "external"]) @@ -167,23 +166,26 @@ # - someone manually coindexes this link (with the md5 in the url) onto # an external page accessible from the package page. # - someone reuploads the package (with a different md5) - # - while easy_installing, an MD5 error occurs because the external link - # is used + # - while easy_installing, an MD5 error occurs because the external + # link is used # -> The index should use the link from pypi, not the external one. # start an index server index_url = server.full_address + '/simple/' # scan a test index - index = simple.SimpleIndex(index_url, follow_externals=True) - dists = index.find("foobar") + crawler = Crawler(index_url, follow_externals=True) + releases = crawler.get_releases("foobar") server.stop() # we have only one link, because links are compared without md5 - self.assertEqual(len(dists), 1) + self.assertEqual(1, len(releases)) + self.assertEqual(1, len(releases[0].dists)) # the link should be from the index - self.assertEqual('12345678901234567', dists[0].url['hashval']) - self.assertEqual('md5', dists[0].url['hashname']) + self.assertEqual(2, len(releases[0].dists['sdist'].urls)) + self.assertEqual('12345678901234567', + releases[0].dists['sdist'].url['hashval']) + self.assertEqual('md5', releases[0].dists['sdist'].url['hashname']) @use_pypi_server(static_filesystem_paths=["with_norel_links"], static_uri_paths=["simple", "external"]) @@ -193,22 +195,22 @@ # to not be processed by the package index, while processing "pages". # process the pages - index = self._get_simple_index(server, follow_externals=True) - index.find("foobar") + crawler = self._get_simple_crawler(server, follow_externals=True) + crawler.get_releases("foobar") # now it should have processed only pages with links rel="download" # and rel="homepage" self.assertIn("%s/simple/foobar/" % server.full_address, - index._processed_urls) # it's the simple index page + crawler._processed_urls) # it's the simple index page self.assertIn("%s/external/homepage.html" % server.full_address, - index._processed_urls) # the external homepage is rel="homepage" + crawler._processed_urls) # the external homepage is rel="homepage" self.assertNotIn("%s/external/nonrel.html" % server.full_address, - index._processed_urls) # this link contains no rel=* + crawler._processed_urls) # this link contains no rel=* self.assertNotIn("%s/unrelated-0.2.tar.gz" % server.full_address, - index._processed_urls) # linked from simple index (no rel) + crawler._processed_urls) # linked from simple index (no rel) self.assertIn("%s/foobar-0.1.tar.gz" % server.full_address, - index._processed_urls) # linked from simple index (rel) + crawler._processed_urls) # linked from simple index (rel) self.assertIn("%s/foobar-2.0.tar.gz" % server.full_address, - index._processed_urls) # linked from external homepage (rel) + crawler._processed_urls) # linked from external homepage (rel) def test_uses_mirrors(self): # When the main repository seems down, try using the given mirrors""" @@ -218,18 +220,18 @@ try: # create the index using both servers - index = simple.SimpleIndex(server.full_address + "/simple/", + crawler = Crawler(server.full_address + "/simple/", hosts=('*',), timeout=1, # set the timeout to 1s for the tests - mirrors=[mirror.full_address + "/simple/",]) + mirrors=[mirror.full_address]) # this should not raise a timeout - self.assertEqual(4, len(index.find("foo"))) + self.assertEqual(4, len(crawler.get_releases("foo"))) finally: mirror.stop() def test_simple_link_matcher(self): # Test that the simple link matcher yields the right links""" - index = simple.SimpleIndex(follow_externals=False) + crawler = Crawler(follow_externals=False) # Here, we define: # 1. one link that must be followed, cause it's a download one @@ -237,27 +239,33 @@ # returns false for it. # 3. one link that must be followed cause it's a homepage that is # browsable - self.assertTrue(index._is_browsable("%stest" % index.index_url)) - self.assertFalse(index._is_browsable("http://dl-link2")) + # 4. one link that must be followed, because it contain a md5 hash + self.assertTrue(crawler._is_browsable("%stest" % crawler.index_url)) + self.assertFalse(crawler._is_browsable("http://dl-link2")) content = """ download_link1 homepage_link1 - homepage_link2 - """ % index.index_url + homepage_link2 + link +link2 +link2 + """ + found_links = dict(crawler._default_link_matcher(content, + base_url)).keys() + self.assertIn('http://example.org/some/homepage', found_links) + self.assertIn('http://example.org/some/simpleurl', found_links) + self.assertIn('http://example.org/some/download', found_links) + + @use_pypi_server("project_list") + def test_search_projects(self, server): + # we can search the index for some projects, on their names + # the case used no matters here + crawler = self._get_simple_crawler(server) + projects = [p.name for p in crawler.search_projects("Foobar")] + self.assertListEqual(['FooBar-bar', 'Foobar-baz', 'Baz-FooBar'], + projects) + def test_suite(): - return unittest.makeSuite(PyPISimpleTestCase) + return unittest.makeSuite(SimpleCrawlerTestCase) if __name__ == '__main__': unittest.main(defaultTest="test_suite") diff --git a/src/distutils2/tests/test_index_xmlrpc.py b/src/distutils2/tests/test_index_xmlrpc.py new file mode 100644 --- /dev/null +++ b/src/distutils2/tests/test_index_xmlrpc.py @@ -0,0 +1,92 @@ +"""Tests for the distutils2.index.xmlrpc module.""" + +from distutils2.tests.pypi_server import use_xmlrpc_server +from distutils2.tests import run_unittest +from distutils2.tests.support import unittest +from distutils2.index.xmlrpc import Client, InvalidSearchField, ProjectNotFound + + +class TestXMLRPCClient(unittest.TestCase): + def _get_client(self, server, *args, **kwargs): + return Client(server.full_address, *args, **kwargs) + + @use_xmlrpc_server() + def test_search_projects(self, server): + client = self._get_client(server) + server.xmlrpc.set_search_result(['FooBar', 'Foo', 'FooFoo']) + results = [r.name for r in client.search_projects(name='Foo')] + self.assertEqual(3, len(results)) + self.assertIn('FooBar', results) + self.assertIn('Foo', results) + self.assertIn('FooFoo', results) + + def test_search_projects_bad_fields(self): + client = Client() + self.assertRaises(InvalidSearchField, client.search_projects, + invalid="test") + + @use_xmlrpc_server() + def test_get_releases(self, server): + client = self._get_client(server) + server.xmlrpc.set_distributions([ + {'name': 'FooBar', 'version': '1.1'}, + {'name': 'FooBar', 'version': '1.2', 'url': 'http://some/url/'}, + {'name': 'FooBar', 'version': '1.3', 'url': 'http://other/url/'}, + ]) + + # use a lambda here to avoid an useless mock call + server.xmlrpc.list_releases = lambda *a, **k: ['1.1', '1.2', '1.3'] + + releases = client.get_releases('FooBar (<=1.2)') + # dont call release_data and release_url; just return name and version. + self.assertEqual(2, len(releases)) + versions = releases.get_versions() + self.assertIn('1.1', versions) + self.assertIn('1.2', versions) + self.assertNotIn('1.3', versions) + + self.assertRaises(ProjectNotFound, client.get_releases,'Foo') + + @use_xmlrpc_server() + def test_get_distributions(self, server): + client = self._get_client(server) + server.xmlrpc.set_distributions([ + {'name':'FooBar', 'version': '1.1', 'url': + 'http://example.org/foobar-1.1-sdist.tar.gz', + 'digest': '1234567', 'type': 'sdist', 'python_version':'source'}, + {'name':'FooBar', 'version': '1.1', 'url': + 'http://example.org/foobar-1.1-bdist.tar.gz', + 'digest': '8912345', 'type': 'bdist'}, + ]) + + releases = client.get_releases('FooBar', '1.1') + client.get_distributions('FooBar', '1.1') + release = releases.get_release('1.1') + self.assertTrue('http://example.org/foobar-1.1-sdist.tar.gz', + release['sdist'].url['url']) + self.assertTrue('http://example.org/foobar-1.1-bdist.tar.gz', + release['bdist'].url['url']) + self.assertEqual(release['sdist'].python_version, 'source') + + @use_xmlrpc_server() + def test_get_metadata(self, server): + client = self._get_client(server) + server.xmlrpc.set_distributions([ + {'name':'FooBar', + 'version': '1.1', + 'keywords': '', + 'obsoletes_dist': ['FooFoo'], + 'requires_external': ['Foo'], + }]) + release = client.get_metadata('FooBar', '1.1') + self.assertEqual(['Foo'], release.metadata['requires_external']) + self.assertEqual(['FooFoo'], release.metadata['obsoletes_dist']) + + +def test_suite(): + suite = unittest.TestSuite() + suite.addTest(unittest.makeSuite(TestXMLRPCClient)) + return suite + +if __name__ == '__main__': + run_unittest(test_suite()) diff --git a/src/distutils2/tests/test_version.py b/src/distutils2/tests/test_version.py --- a/src/distutils2/tests/test_version.py +++ b/src/distutils2/tests/test_version.py @@ -188,6 +188,13 @@ # XXX need to silent the micro version in this case #assert not VersionPredicate('Ho (<3.0,!=2.6)').match('2.6.3') + def test_predicate_name(self): + # Test that names are parsed the right way + + self.assertEqual('Hey', VersionPredicate('Hey (<1.1)').name) + self.assertEqual('Foo-Bar', VersionPredicate('Foo-Bar (1.1)').name) + self.assertEqual('Foo Bar', VersionPredicate('Foo Bar (1.1)').name) + def test_is_final(self): # VersionPredicate knows is a distribution is a final one or not. final_versions = ('1.0', '1.0.post456') diff --git a/src/distutils2/util.py b/src/distutils2/util.py --- a/src/distutils2/util.py +++ b/src/distutils2/util.py @@ -5,10 +5,14 @@ __revision__ = "$Id: util.py 77761 2010-01-26 22:46:15Z tarek.ziade $" +import os +import posixpath +import re +import string import sys -import os -import string -import re +import shutil +import tarfile +import zipfile from copy import copy from fnmatch import fnmatchcase from ConfigParser import RawConfigParser @@ -699,6 +703,119 @@ self.options, self.explicit) +def splitext(path): + """Like os.path.splitext, but take off .tar too""" + base, ext = posixpath.splitext(path) + if base.lower().endswith('.tar'): + ext = base[-4:] + ext + base = base[:-4] + return base, ext + + +def unzip_file(filename, location, flatten=True): + """Unzip the file (zip file located at filename) to the destination + location""" + if not os.path.exists(location): + os.makedirs(location) + zipfp = open(filename, 'rb') + try: + zip = zipfile.ZipFile(zipfp) + leading = has_leading_dir(zip.namelist()) and flatten + for name in zip.namelist(): + data = zip.read(name) + fn = name + if leading: + fn = split_leading_dir(name)[1] + fn = os.path.join(location, fn) + dir = os.path.dirname(fn) + if not os.path.exists(dir): + os.makedirs(dir) + if fn.endswith('/') or fn.endswith('\\'): + # A directory + if not os.path.exists(fn): + os.makedirs(fn) + else: + fp = open(fn, 'wb') + try: + fp.write(data) + finally: + fp.close() + finally: + zipfp.close() + + +def untar_file(filename, location): + """Untar the file (tar file located at filename) to the destination + location + """ + if not os.path.exists(location): + os.makedirs(location) + if filename.lower().endswith('.gz') or filename.lower().endswith('.tgz'): + mode = 'r:gz' + elif (filename.lower().endswith('.bz2') + or filename.lower().endswith('.tbz')): + mode = 'r:bz2' + elif filename.lower().endswith('.tar'): + mode = 'r' + else: + mode = 'r:*' + tar = tarfile.open(filename, mode) + try: + leading = has_leading_dir([member.name for member in tar.getmembers()]) + for member in tar.getmembers(): + fn = member.name + if leading: + fn = split_leading_dir(fn)[1] + path = os.path.join(location, fn) + if member.isdir(): + if not os.path.exists(path): + os.makedirs(path) + else: + try: + fp = tar.extractfile(member) + except (KeyError, AttributeError), e: + # Some corrupt tar files seem to produce this + # (specifically bad symlinks) + continue + if not os.path.exists(os.path.dirname(path)): + os.makedirs(os.path.dirname(path)) + destfp = open(path, 'wb') + try: + shutil.copyfileobj(fp, destfp) + finally: + destfp.close() + fp.close() + finally: + tar.close() + + +def has_leading_dir(paths): + """Returns true if all the paths have the same leading path name + (i.e., everything is in one subdirectory in an archive)""" + common_prefix = None + for path in paths: + prefix, rest = split_leading_dir(path) + if not prefix: + return False + elif common_prefix is None: + common_prefix = prefix + elif prefix != common_prefix: + return False + return True + + +def split_leading_dir(path): + path = str(path) + path = path.lstrip('/').lstrip('\\') + if '/' in path and (('\\' in path and path.find('/') < path.find('\\')) + or '\\' not in path): + return path.split('/', 1) + elif '\\' in path: + return path.split('\\', 1) + else: + return path, '' + + def spawn(cmd, search_path=1, verbose=0, dry_run=0, env=None): """Run another program specified as a command list 'cmd' in a new process. diff --git a/src/distutils2/version.py b/src/distutils2/version.py --- a/src/distutils2/version.py +++ b/src/distutils2/version.py @@ -321,7 +321,7 @@ return None -_PREDICATE = re.compile(r"(?i)^\s*([a-z_]\w*(?:\.[a-z_]\w*)*)(.*)") +_PREDICATE = re.compile(r"(?i)^\s*([a-z_][\sa-zA-Z_-]*(?:\.[a-z_]\w*)*)(.*)") _VERSIONS = re.compile(r"^\s*\((.*)\)\s*$") _PLAIN_VERSIONS = re.compile(r"^\s*(.*)\s*$") _SPLIT_CMP = re.compile(r"^\s*(<=|>=|<|>|!=|==)\s*([^\s,]+)\s*$") @@ -354,7 +354,8 @@ if match is None: raise ValueError('Bad predicate "%s"' % predicate) - self.name, predicates = match.groups() + name, predicates = match.groups() + self.name = name.strip() predicates = predicates.strip() predicates = _VERSIONS.match(predicates) if predicates is not None: -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:47 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:47 +0200 Subject: [Python-checkins] distutils2: improved support for coverage 2.x Message-ID: tarek.ziade pushed 2a5ed12ac6db to distutils2: http://hg.python.org/distutils2/rev/2a5ed12ac6db changeset: 486:2a5ed12ac6db user: Yannick Gingras date: Thu Aug 05 22:18:07 2010 -0400 summary: improved support for coverage 2.x files: src/runtests-cov.py diff --git a/src/runtests-cov.py b/src/runtests-cov.py --- a/src/runtests-cov.py +++ b/src/runtests-cov.py @@ -5,16 +5,18 @@ """ import sys -from os.path import dirname, islink, realpath +from os.path import dirname, islink, realpath, join, abspath from optparse import OptionParser +COVERAGE_FILE = join(dirname(abspath(__file__)), '.coverage') + def get_coverage(): """ Return a usable coverage object. """ # deferred import because coverage is optional import coverage cov = getattr(coverage, "the_coverage", None) if not cov: - cov = coverage.coverage() + cov = coverage.coverage(COVERAGE_FILE) return cov def ignore_prefixes(module): @@ -61,6 +63,7 @@ else: morfs = "distutils2/*" # running coverage 2.x + cov.cache = COVERAGE_FILE cov.restore() prefixes = ["runtests", "distutils2/tests", "distutils2/_backport"] -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:47 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:47 +0200 Subject: [Python-checkins] distutils2: added a test for bad options to Distribution() Message-ID: tarek.ziade pushed 11a1f110d6c2 to distutils2: http://hg.python.org/distutils2/rev/11a1f110d6c2 changeset: 482:11a1f110d6c2 user: Yannick Gingras date: Fri Jul 30 00:52:53 2010 -0400 summary: added a test for bad options to Distribution() files: src/distutils2/tests/test_dist.py diff --git a/src/distutils2/tests/test_dist.py b/src/distutils2/tests/test_dist.py --- a/src/distutils2/tests/test_dist.py +++ b/src/distutils2/tests/test_dist.py @@ -153,6 +153,27 @@ my_file2 = os.path.join(tmp_dir, 'f2') dist.metadata.write_file(open(my_file, 'w')) + def test_bad_attr(self): + cls = Distribution + + # catching warnings + warns = [] + def _warn(msg): + warns.append(msg) + + old_warn = warnings.warn + warnings.warn = _warn + try: + dist = cls(attrs={'author': 'xxx', + 'name': 'xxx', + 'version': 'xxx', + 'url': 'xxxx', + 'badoptname': 'xxx'}) + finally: + warnings.warn = old_warn + + self.assertTrue(len(warns)==1 and "Unknown distribution" in warns[0]) + def test_empty_options(self): # an empty options dictionary should not stay in the # list of attributes -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:47 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:47 +0200 Subject: [Python-checkins] distutils2: added support for coverage 2.x Message-ID: tarek.ziade pushed 00e9fa925d22 to distutils2: http://hg.python.org/distutils2/rev/00e9fa925d22 changeset: 485:00e9fa925d22 user: Yannick Gingras date: Thu Aug 05 20:47:04 2010 -0400 summary: added support for coverage 2.x files: src/runtests-cov.py diff --git a/src/runtests-cov.py b/src/runtests-cov.py --- a/src/runtests-cov.py +++ b/src/runtests-cov.py @@ -8,6 +8,14 @@ from os.path import dirname, islink, realpath from optparse import OptionParser +def get_coverage(): + """ Return a usable coverage object. """ + # deferred import because coverage is optional + import coverage + cov = getattr(coverage, "the_coverage", None) + if not cov: + cov = coverage.coverage() + return cov def ignore_prefixes(module): """ Return a list of prefixes to ignore in the coverage report if @@ -44,10 +52,16 @@ def coverage_report(opts): - import coverage from distutils2.tests.support import unittest - cov = coverage.coverage() - cov.load() + cov = get_coverage() + if hasattr(cov, "load"): + # running coverage 3.x + cov.load() + morfs = None + else: + morfs = "distutils2/*" + # running coverage 2.x + cov.restore() prefixes = ["runtests", "distutils2/tests", "distutils2/_backport"] prefixes += ignore_prefixes(unittest) @@ -66,7 +80,7 @@ # that module is also completely optional pass - cov.report(omit_prefixes=prefixes, show_missing=opts.show_missing) + cov.report(morfs, omit_prefixes=prefixes, show_missing=opts.show_missing) def test_main(): @@ -74,11 +88,8 @@ verbose = not opts.quiet ret = 0 - if opts.coverage or opts.report: - import coverage - if opts.coverage: - cov = coverage.coverage() + cov = get_coverage() cov.erase() cov.start() if not opts.report: -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:47 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:47 +0200 Subject: [Python-checkins] distutils2: Automated merge with ssh://hg@bitbucket.org/tarek/distutils2 Message-ID: tarek.ziade pushed f634457a13be to distutils2: http://hg.python.org/distutils2/rev/f634457a13be changeset: 483:f634457a13be parent: 479:7debb069fc21 parent: 482:11a1f110d6c2 user: Yannick Gingras date: Thu Aug 05 19:17:10 2010 -0400 summary: Automated merge with ssh://hg at bitbucket.org/tarek/distutils2 files: src/distutils2/tests/test_dist.py diff --git a/src/distutils2/tests/test_dist.py b/src/distutils2/tests/test_dist.py --- a/src/distutils2/tests/test_dist.py +++ b/src/distutils2/tests/test_dist.py @@ -153,6 +153,27 @@ my_file2 = os.path.join(tmp_dir, 'f2') dist.metadata.write_file(open(my_file, 'w')) + def test_bad_attr(self): + cls = Distribution + + # catching warnings + warns = [] + def _warn(msg): + warns.append(msg) + + old_warn = warnings.warn + warnings.warn = _warn + try: + dist = cls(attrs={'author': 'xxx', + 'name': 'xxx', + 'version': 'xxx', + 'url': 'xxxx', + 'badoptname': 'xxx'}) + finally: + warnings.warn = old_warn + + self.assertTrue(len(warns)==1 and "Unknown distribution" in warns[0]) + def test_empty_options(self): # an empty options dictionary should not stay in the # list of attributes @@ -176,6 +197,21 @@ self.assertEqual(len(warns), 0) + def test_non_empty_options(self): + # TODO: how to actually use options is not documented except + # for a few cryptic comments in dist.py. If this is to stay + # in the public API, it deserves some better documentation. + + # Here is an example of how it's used out there: + # http://svn.pythonmac.org/py2app/py2app/trunk/doc/index.html#specifying-customizations + cls = Distribution + dist = cls(attrs={'author': 'xxx', + 'name': 'xxx', + 'version': 'xxx', + 'url': 'xxxx', + 'options': dict(sdist=dict(owner="root"))}) + self.assertTrue("owner" in dist.get_option_dict("sdist")) + def test_finalize_options(self): attrs = {'keywords': 'one,two', -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:47 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:47 +0200 Subject: [Python-checkins] distutils2: Merges changes Message-ID: tarek.ziade pushed 475e8d8deddf to distutils2: http://hg.python.org/distutils2/rev/475e8d8deddf changeset: 488:475e8d8deddf parent: 485:00e9fa925d22 parent: 487:e0044dfa1e00 user: Antoine Reversat date: Thu Aug 05 23:01:33 2010 -0400 summary: Merges changes files: src/distutils2/__init__.py, src/distutils2/core.py, src/distutils2/dist.py, src/distutils2/metadata.py, src/distutils2/mkpkg.py diff --git a/src/distutils2/__init__.py b/src/distutils2/__init__.py --- a/src/distutils2/__init__.py +++ b/src/distutils2/__init__.py @@ -20,7 +20,7 @@ __version__ = "1.0a2" -# when set to True, converts doctests by default too +# when set to True, converts doctests by default too run_2to3_on_doctests = True # Standard package names for fixer packages lib2to3_fixer_packages = ['lib2to3.fixes'] diff --git a/src/distutils2/core.py b/src/distutils2/core.py --- a/src/distutils2/core.py +++ b/src/distutils2/core.py @@ -33,6 +33,7 @@ or: %(script)s cmd --help """ + def gen_usage(script_name): script = os.path.basename(script_name) return USAGE % {'script': script} @@ -59,6 +60,7 @@ 'extra_objects', 'extra_compile_args', 'extra_link_args', 'swig_opts', 'export_symbols', 'depends', 'language') + def setup(**attrs): """The gateway to the Distutils: do everything your setup script needs to do, in a highly flexible and user-driven way. Briefly: create a diff --git a/src/distutils2/depgraph.py b/src/distutils2/depgraph.py --- a/src/distutils2/depgraph.py +++ b/src/distutils2/depgraph.py @@ -1,5 +1,5 @@ -"""Analyse the relationships between the distributions in the system and generate -a dependency graph. +"""Analyse the relationships between the distributions in the system +and generate a dependency graph. """ from distutils2.errors import DistutilsError diff --git a/src/distutils2/dist.py b/src/distutils2/dist.py --- a/src/distutils2/dist.py +++ b/src/distutils2/dist.py @@ -6,7 +6,9 @@ __revision__ = "$Id: dist.py 77717 2010-01-24 00:33:32Z tarek.ziade $" -import sys, os, re +import sys +import os +import re import warnings from ConfigParser import RawConfigParser @@ -22,7 +24,8 @@ # the same as a Python NAME -- I don't allow leading underscores. The fact # that they're very similar is no coincidence; the default naming scheme is # to look for a Python module named after the command. -command_re = re.compile (r'^[a-zA-Z]([a-zA-Z0-9_]*)$') +command_re = re.compile(r'^[a-zA-Z]([a-zA-Z0-9_]*)$') + class Distribution(object): """The core of the Distutils. Most of the work hiding behind 'setup' @@ -119,10 +122,8 @@ # negative options are options that exclude other options negative_opt = {'quiet': 'verbose'} - # -- Creation/initialization methods ------------------------------- - - def __init__ (self, attrs=None): + def __init__(self, attrs=None): """Construct a new Distribution instance: initialize all the attributes of a Distribution, and then use 'attrs' (a dictionary mapping attribute names to values) to assign some of those @@ -558,10 +559,10 @@ if (hasattr(cmd_class, 'help_options') and isinstance(cmd_class.help_options, list)): - help_option_found=0 + help_option_found = 0 for (help_option, short, desc, func) in cmd_class.help_options: if hasattr(opts, parser.get_attr_name(help_option)): - help_option_found=1 + help_option_found = 1 if hasattr(func, '__call__'): func() else: @@ -800,7 +801,7 @@ class_name = command try: - __import__ (module_name) + __import__(module_name) module = sys.modules[module_name] except ImportError: continue @@ -817,7 +818,6 @@ raise DistutilsModuleError("invalid command '%s'" % command) - def get_command_obj(self, command, create=1): """Return the command object for 'command'. Normally this object is cached on a previous call to 'get_command_obj()'; if no command @@ -960,7 +960,6 @@ self.run_command_hooks(cmd_obj, 'post_hook') self.have_run[command] = 1 - def run_command_hooks(self, cmd_obj, hook_kind): hooks = getattr(cmd_obj, hook_kind) if hooks is None: @@ -970,7 +969,6 @@ hook_func(cmd_obj) # -- Distribution query methods ------------------------------------ - def has_pure_modules(self): return len(self.packages or self.py_modules or []) > 0 diff --git a/src/distutils2/errors.py b/src/distutils2/errors.py --- a/src/distutils2/errors.py +++ b/src/distutils2/errors.py @@ -10,31 +10,38 @@ __revision__ = "$Id: errors.py 75901 2009-10-28 06:45:18Z tarek.ziade $" + class DistutilsError(Exception): """The root of all Distutils evil.""" + class DistutilsModuleError(DistutilsError): """Unable to load an expected module, or to find an expected class within some module (in particular, command modules and classes).""" + class DistutilsClassError(DistutilsError): """Some command class (or possibly distribution class, if anyone feels a need to subclass Distribution) is found not to be holding up its end of the bargain, ie. implementing some part of the "command "interface.""" + class DistutilsGetoptError(DistutilsError): """The option table provided to 'fancy_getopt()' is bogus.""" + class DistutilsArgError(DistutilsError): """Raised by fancy_getopt in response to getopt.error -- ie. an error in the command line usage.""" + class DistutilsFileError(DistutilsError): """Any problems in the filesystem: expected file not found, etc. Typically this is for problems that we detect before IOError or OSError could be raised.""" + class DistutilsOptionError(DistutilsError): """Syntactic/semantic errors in command options, such as use of mutually conflicting options, or inconsistent options, @@ -43,60 +50,76 @@ files, or what-have-you -- but if we *know* something originated in the setup script, we'll raise DistutilsSetupError instead.""" + class DistutilsSetupError(DistutilsError): """For errors that can be definitely blamed on the setup script, such as invalid keyword arguments to 'setup()'.""" + class DistutilsPlatformError(DistutilsError): """We don't know how to do something on the current platform (but we do know how to do it on some platform) -- eg. trying to compile C files on a platform not supported by a CCompiler subclass.""" + class DistutilsExecError(DistutilsError): """Any problems executing an external program (such as the C compiler, when compiling C files).""" + class DistutilsInternalError(DistutilsError): """Internal inconsistencies or impossibilities (obviously, this should never be seen if the code is working!).""" + class DistutilsTemplateError(DistutilsError): """Syntax error in a file list template.""" + class DistutilsByteCompileError(DistutilsError): """Byte compile error.""" + # Exception classes used by the CCompiler implementation classes class CCompilerError(Exception): """Some compile/link operation failed.""" + class PreprocessError(CCompilerError): """Failure to preprocess one or more C/C++ files.""" + class CompileError(CCompilerError): """Failure to compile one or more C/C++ source files.""" + class LibError(CCompilerError): """Failure to create a static library from one or more C/C++ object files.""" + class LinkError(CCompilerError): """Failure to link one or more C/C++ object files into an executable or shared library file.""" + class UnknownFileError(CCompilerError): """Attempt to process an unknown file type.""" + class MetadataConflictError(DistutilsError): """Attempt to read or write metadata fields that are conflictual.""" + class MetadataUnrecognizedVersionError(DistutilsError): """Unknown metadata version number.""" + class IrrationalVersionError(Exception): """This is an irrational version.""" pass + class HugeMajorVersionNumError(IrrationalVersionError): """An irrational version because the major version number is huge (often because a year or date was used). @@ -105,4 +128,3 @@ This guard can be disabled by setting that option False. """ pass - diff --git a/src/distutils2/extension.py b/src/distutils2/extension.py --- a/src/distutils2/extension.py +++ b/src/distutils2/extension.py @@ -17,6 +17,7 @@ # import that large-ish module (indirectly, through distutils.core) in # order to do anything. + class Extension(object): """Just a collection of attributes that describes an extension module and everything needed to build it (hopefully in a portable @@ -84,7 +85,7 @@ # When adding arguments to this constructor, be sure to update # setup_keywords in core.py. - def __init__ (self, name, sources, + def __init__(self, name, sources, include_dirs=None, define_macros=None, undef_macros=None, @@ -95,11 +96,11 @@ extra_compile_args=None, extra_link_args=None, export_symbols=None, - swig_opts = None, + swig_opts=None, depends=None, language=None, optional=None, - **kw # To catch unknown keywords + **kw # To catch unknown keywords ): if not isinstance(name, str): raise AssertionError("'name' must be a string") @@ -134,4 +135,3 @@ options = ', '.join(sorted(options)) msg = "Unknown Extension options: %s" % options warnings.warn(msg) - diff --git a/src/distutils2/fancy_getopt.py b/src/distutils2/fancy_getopt.py --- a/src/distutils2/fancy_getopt.py +++ b/src/distutils2/fancy_getopt.py @@ -30,6 +30,7 @@ # (for use as attributes of some object). longopt_xlate = string.maketrans('-', '_') + class FancyGetopt(object): """Wrapper around the standard 'getopt()' module that provides some handy extra functionality: @@ -42,7 +43,7 @@ on the command line sets 'verbose' to false """ - def __init__ (self, option_table=None): + def __init__(self, option_table=None): # The option table is (currently) a list of tuples. The # tuples may have 3 or four values: @@ -180,7 +181,8 @@ self.long_opts.append(long) if long[-1] == '=': # option takes an argument? - if short: short = short + ':' + if short: + short = short + ':' long = long[0:-1] self.takes_arg[long] = 1 else: diff --git a/src/distutils2/manifest.py b/src/distutils2/manifest.py --- a/src/distutils2/manifest.py +++ b/src/distutils2/manifest.py @@ -22,7 +22,7 @@ # a \ followed by some spaces + EOL _COLLAPSE_PATTERN = re.compile('\\\w*\n', re.M) -_COMMENTED_LINE = re.compile('#.*?(?=\n)|^\w*\n|\n(?=$)', re.M|re.S) +_COMMENTED_LINE = re.compile('#.*?(?=\n)|^\w*\n|\n(?=$)', re.M | re.S) class Manifest(object): """A list of files built by on exploring the filesystem and filtered by diff --git a/src/distutils2/metadata.py b/src/distutils2/metadata.py --- a/src/distutils2/metadata.py +++ b/src/distutils2/metadata.py @@ -52,12 +52,12 @@ PKG_INFO_PREFERRED_VERSION = '1.0' _LINE_PREFIX = re.compile('\n \|') -_241_FIELDS = ('Metadata-Version', 'Name', 'Version', 'Platform', +_241_FIELDS = ('Metadata-Version', 'Name', 'Version', 'Platform', 'Summary', 'Description', 'Keywords', 'Home-page', 'Author', 'Author-email', 'License') -_314_FIELDS = ('Metadata-Version', 'Name', 'Version', 'Platform', +_314_FIELDS = ('Metadata-Version', 'Name', 'Version', 'Platform', 'Supported-Platform', 'Summary', 'Description', 'Keywords', 'Home-page', 'Author', 'Author-email', 'License', 'Classifier', 'Download-URL', 'Obsoletes', @@ -65,7 +65,7 @@ _314_MARKERS = ('Obsoletes', 'Provides', 'Requires') -_345_FIELDS = ('Metadata-Version', 'Name', 'Version', 'Platform', +_345_FIELDS = ('Metadata-Version', 'Name', 'Version', 'Platform', 'Supported-Platform', 'Summary', 'Description', 'Keywords', 'Home-page', 'Author', 'Author-email', 'Maintainer', 'Maintainer-email', 'License', diff --git a/src/distutils2/mkpkg.py b/src/distutils2/mkpkg.py --- a/src/distutils2/mkpkg.py +++ b/src/distutils2/mkpkg.py @@ -30,7 +30,11 @@ # # Detect scripts (not sure how. #! outside of package?) -import sys, os, re, shutil, ConfigParser +import sys +import os +import re +import shutil +import ConfigParser helpText = { @@ -629,19 +633,20 @@ print '\nERROR: You must select "Y" or "N".\n' -def ask(question, default = None, helptext = None, required = True, - lengthy = False, multiline = False): - prompt = '%s: ' % ( question, ) +def ask(question, default=None, helptext=None, required=True, + lengthy=False, multiline=False): + prompt = '%s: ' % (question,) if default: - prompt = '%s [%s]: ' % ( question, default ) + prompt = '%s [%s]: ' % (question, default) if default and len(question) + len(default) > 70: - prompt = '%s\n [%s]: ' % ( question, default ) + prompt = '%s\n [%s]: ' % (question, default) if lengthy or multiline: prompt += '\n >' - if not helptext: helptext = 'No additional help available.' - if helptext[0] == '\n': helptext = helptext[1:] - if helptext[-1] == '\n': helptext = helptext[:-1] + if not helptext: + helptext = 'No additional help available.' + + helptext = helptext.strip("\n") while True: sys.stdout.write(prompt) @@ -653,12 +658,14 @@ print helptext print '=' * 70 continue - if default and not line: return(default) + if default and not line: + return(default) if not line and required: print '*' * 70 print 'This value cannot be empty.' print '===========================' - if helptext: print helptext + if helptext: + print helptext print '*' * 70 continue return(line) @@ -669,7 +676,8 @@ for key in troveList: subDict = dict for subkey in key.split(' :: '): - if not subkey in subDict: subDict[subkey] = {} + if not subkey in subDict: + subDict[subkey] = {} subDict = subDict[subkey] return(dict) troveDict = buildTroveDict(troveList) @@ -687,7 +695,8 @@ def lookupOption(self, key): - if not self.config.has_option('DEFAULT', key): return(None) + if not self.config.has_option('DEFAULT', key): + return(None) return(self.config.get('DEFAULT', key)) @@ -706,7 +715,8 @@ self.config.set('DEFAULT', compareKey, self.setupData[compareKey]) - if not valuesDifferent: return + if not valuesDifferent: + return self.config.write(open(os.path.expanduser('~/.pygiver'), 'w')) @@ -718,7 +728,7 @@ def inspectFile(self, path): fp = open(path, 'r') try: - for line in [ fp.readline() for x in range(10) ]: + for line in [fp.readline() for x in range(10)]: m = re.match(r'^#!.*python((?P\d)(\.\d+)?)?$', line) if m: if m.group('major') == '3': @@ -761,16 +771,16 @@ self.setupData.get('version'), helpText['version']) self.setupData['description'] = ask('Package description', self.setupData.get('description'), helpText['description'], - lengthy = True) + lengthy=True) self.setupData['author'] = ask('Author name', self.setupData.get('author'), helpText['author']) self.setupData['author_email'] = ask('Author e-mail address', self.setupData.get('author_email'), helpText['author_email']) self.setupData['url'] = ask('Project URL', - self.setupData.get('url'), helpText['url'], required = False) + self.setupData.get('url'), helpText['url'], required=False) if (askYn('Do you want to set Trove classifiers?', - helptext = helpText['do_classifier']) == 'y'): + helptext=helpText['do_classifier']) == 'y'): self.setTroveClassifier() @@ -781,8 +791,10 @@ def setTroveOther(self, classifierDict): - if askYn('Do you want to set other trove identifiers', 'n', - helpText['trove_generic']) != 'y': return + if askYn('Do you want to set other trove identifiers', + 'n', + helpText['trove_generic']) != 'y': + return self.walkTrove(classifierDict, [troveDict], '') @@ -799,7 +811,7 @@ continue if askYn('Do you want to set items under\n "%s" (%d sub-items)' - % ( key, len(trove[key]) ), 'n', + % (key, len(trove[key])), 'n', helpText['trove_generic']) == 'y': self.walkTrove(classifierDict, trovePath + [trove[key]], desc + ' :: ' + key) @@ -808,15 +820,18 @@ def setTroveLicense(self, classifierDict): while True: license = ask('What license do you use', - helptext = helpText['trove_license'], required = False) - if not license: return + helptext=helpText['trove_license'], + required=False) + if not license: + return licenseWords = license.lower().split(' ') foundList = [] for index in range(len(troveList)): troveItem = troveList[index] - if not troveItem.startswith('License :: '): continue + if not troveItem.startswith('License :: '): + continue troveItem = troveItem[11:].lower() allMatch = True @@ -824,17 +839,20 @@ if not word in troveItem: allMatch = False break - if allMatch: foundList.append(index) + if allMatch: + foundList.append(index) question = 'Matching licenses:\n\n' for i in xrange(1, len(foundList) + 1): - question += ' %s) %s\n' % ( i, troveList[foundList[i - 1]] ) + question += ' %s) %s\n' % (i, troveList[foundList[i - 1]]) question += ('\nType the number of the license you wish to use or ' '? to try again:') - troveLicense = ask(question, required = False) + troveLicense = ask(question, required=False) - if troveLicense == '?': continue - if troveLicense == '': return + if troveLicense == '?': + continue + if troveLicense == '': + return foundIndex = foundList[int(troveLicense) - 1] classifierDict[troveList[foundIndex]] = 1 try: @@ -856,7 +874,7 @@ 6 - Mature 7 - Inactive -Status''', required = False) +Status''', required=False) if devStatus: try: key = { @@ -884,7 +902,8 @@ return modified_pkgs def writeSetup(self): - if os.path.exists('setup.py'): shutil.move('setup.py', 'setup.py.old') + if os.path.exists('setup.py'): + shutil.move('setup.py', 'setup.py.old') fp = open('setup.py', 'w') try: -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:47 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:47 +0200 Subject: [Python-checkins] distutils2: Changed some files to conform to PEP8 Message-ID: tarek.ziade pushed e0044dfa1e00 to distutils2: http://hg.python.org/distutils2/rev/e0044dfa1e00 changeset: 487:e0044dfa1e00 parent: 395:db89914133c6 user: Antoine Reversat date: Thu Aug 05 21:49:06 2010 -0400 summary: Changed some files to conform to PEP8 files: src/distutils2/__init__.py, src/distutils2/core.py, src/distutils2/depgraph.py, src/distutils2/dist.py, src/distutils2/errors.py, src/distutils2/extension.py, src/distutils2/fancy_getopt.py, src/distutils2/manifest.py, src/distutils2/metadata.py, src/distutils2/mkpkg.py diff --git a/src/distutils2/__init__.py b/src/distutils2/__init__.py --- a/src/distutils2/__init__.py +++ b/src/distutils2/__init__.py @@ -16,11 +16,12 @@ """ __all__ = ['__version__'] -__revision__ = "$Id: __init__.py 78020 2010-02-06 16:37:32Z benjamin.peterson $" +__revision__ = \ + "$Id: __init__.py 78020 2010-02-06 16:37:32Z benjamin.peterson $" __version__ = "1.0a2" -# when set to True, converts doctests by default too +# when set to True, converts doctests by default too run_2to3_on_doctests = True # Standard package names for fixer packages lib2to3_fixer_packages = ['lib2to3.fixes'] diff --git a/src/distutils2/core.py b/src/distutils2/core.py --- a/src/distutils2/core.py +++ b/src/distutils2/core.py @@ -33,6 +33,7 @@ or: %(script)s cmd --help """ + def gen_usage(script_name): script = os.path.basename(script_name) return USAGE % {'script': script} @@ -59,6 +60,7 @@ 'extra_objects', 'extra_compile_args', 'extra_link_args', 'swig_opts', 'export_symbols', 'depends', 'language') + def setup(**attrs): """The gateway to the Distutils: do everything your setup script needs to do, in a highly flexible and user-driven way. Briefly: create a diff --git a/src/distutils2/depgraph.py b/src/distutils2/depgraph.py --- a/src/distutils2/depgraph.py +++ b/src/distutils2/depgraph.py @@ -1,5 +1,5 @@ -"""Analyse the relationships between the distributions in the system and generate -a dependency graph. +"""Analyse the relationships between the distributions in the system +and generate a dependency graph. """ from distutils2.errors import DistutilsError diff --git a/src/distutils2/dist.py b/src/distutils2/dist.py --- a/src/distutils2/dist.py +++ b/src/distutils2/dist.py @@ -6,7 +6,9 @@ __revision__ = "$Id: dist.py 77717 2010-01-24 00:33:32Z tarek.ziade $" -import sys, os, re +import sys +import os +import re try: import warnings @@ -26,7 +28,7 @@ # the same as a Python NAME -- I don't allow leading underscores. The fact # that they're very similar is no coincidence; the default naming scheme is # to look for a Python module named after the command. -command_re = re.compile (r'^[a-zA-Z]([a-zA-Z0-9_]*)$') +command_re = re.compile(r'^[a-zA-Z]([a-zA-Z0-9_]*)$') class Distribution(object): @@ -116,7 +118,7 @@ ('use-2to3', None, "use 2to3 to make source python 3.x compatible"), ('convert-2to3-doctests', None, - "use 2to3 to convert doctests in seperate text files"), + "use 2to3 to convert doctests in seperate text files"), ] display_option_names = map(lambda x: translate_longopt(x[0]), display_options) @@ -124,10 +126,8 @@ # negative options are options that exclude other options negative_opt = {'quiet': 'verbose'} - # -- Creation/initialization methods ------------------------------- - - def __init__ (self, attrs=None): + def __init__(self, attrs=None): """Construct a new Distribution instance: initialize all the attributes of a Distribution, and then use 'attrs' (a dictionary mapping attribute names to values) to assign some of those @@ -381,7 +381,7 @@ for opt in options: if opt != '__name__': - val = parser.get(section,opt) + val = parser.get(section, opt) opt = opt.replace('-', '_') opt_dict[opt] = (filename, val) @@ -555,10 +555,10 @@ if (hasattr(cmd_class, 'help_options') and isinstance(cmd_class.help_options, list)): - help_option_found=0 + help_option_found = 0 for (help_option, short, desc, func) in cmd_class.help_options: if hasattr(opts, parser.get_attr_name(help_option)): - help_option_found=1 + help_option_found = 1 if hasattr(func, '__call__'): func() else: @@ -584,7 +584,7 @@ objects. """ if getattr(self, 'convert_2to3_doctests', None): - self.convert_2to3_doctests = [os.path.join(p) + self.convert_2to3_doctests = [os.path.join(p) for p in self.convert_2to3_doctests] else: self.convert_2to3_doctests = [] @@ -797,7 +797,7 @@ class_name = command try: - __import__ (module_name) + __import__(module_name) module = sys.modules[module_name] except ImportError: continue @@ -814,7 +814,6 @@ raise DistutilsModuleError("invalid command '%s'" % command) - def get_command_obj(self, command, create=1): """Return the command object for 'command'. Normally this object is cached on a previous call to 'get_command_obj()'; if no command @@ -955,9 +954,7 @@ cmd_obj.run() self.have_run[command] = 1 - # -- Distribution query methods ------------------------------------ - def has_pure_modules(self): return len(self.packages or self.py_modules or []) > 0 diff --git a/src/distutils2/errors.py b/src/distutils2/errors.py --- a/src/distutils2/errors.py +++ b/src/distutils2/errors.py @@ -10,31 +10,38 @@ __revision__ = "$Id: errors.py 75901 2009-10-28 06:45:18Z tarek.ziade $" + class DistutilsError(Exception): """The root of all Distutils evil.""" + class DistutilsModuleError(DistutilsError): """Unable to load an expected module, or to find an expected class within some module (in particular, command modules and classes).""" + class DistutilsClassError(DistutilsError): """Some command class (or possibly distribution class, if anyone feels a need to subclass Distribution) is found not to be holding up its end of the bargain, ie. implementing some part of the "command "interface.""" + class DistutilsGetoptError(DistutilsError): """The option table provided to 'fancy_getopt()' is bogus.""" + class DistutilsArgError(DistutilsError): """Raised by fancy_getopt in response to getopt.error -- ie. an error in the command line usage.""" + class DistutilsFileError(DistutilsError): """Any problems in the filesystem: expected file not found, etc. Typically this is for problems that we detect before IOError or OSError could be raised.""" + class DistutilsOptionError(DistutilsError): """Syntactic/semantic errors in command options, such as use of mutually conflicting options, or inconsistent options, @@ -43,60 +50,76 @@ files, or what-have-you -- but if we *know* something originated in the setup script, we'll raise DistutilsSetupError instead.""" + class DistutilsSetupError(DistutilsError): """For errors that can be definitely blamed on the setup script, such as invalid keyword arguments to 'setup()'.""" + class DistutilsPlatformError(DistutilsError): """We don't know how to do something on the current platform (but we do know how to do it on some platform) -- eg. trying to compile C files on a platform not supported by a CCompiler subclass.""" + class DistutilsExecError(DistutilsError): """Any problems executing an external program (such as the C compiler, when compiling C files).""" + class DistutilsInternalError(DistutilsError): """Internal inconsistencies or impossibilities (obviously, this should never be seen if the code is working!).""" + class DistutilsTemplateError(DistutilsError): """Syntax error in a file list template.""" + class DistutilsByteCompileError(DistutilsError): """Byte compile error.""" + # Exception classes used by the CCompiler implementation classes class CCompilerError(Exception): """Some compile/link operation failed.""" + class PreprocessError(CCompilerError): """Failure to preprocess one or more C/C++ files.""" + class CompileError(CCompilerError): """Failure to compile one or more C/C++ source files.""" + class LibError(CCompilerError): """Failure to create a static library from one or more C/C++ object files.""" + class LinkError(CCompilerError): """Failure to link one or more C/C++ object files into an executable or shared library file.""" + class UnknownFileError(CCompilerError): """Attempt to process an unknown file type.""" + class MetadataConflictError(DistutilsError): """Attempt to read or write metadata fields that are conflictual.""" + class MetadataUnrecognizedVersionError(DistutilsError): """Unknown metadata version number.""" + class IrrationalVersionError(Exception): """This is an irrational version.""" pass + class HugeMajorVersionNumError(IrrationalVersionError): """An irrational version because the major version number is huge (often because a year or date was used). @@ -105,4 +128,3 @@ This guard can be disabled by setting that option False. """ pass - diff --git a/src/distutils2/extension.py b/src/distutils2/extension.py --- a/src/distutils2/extension.py +++ b/src/distutils2/extension.py @@ -17,6 +17,7 @@ # import that large-ish module (indirectly, through distutils.core) in # order to do anything. + class Extension(object): """Just a collection of attributes that describes an extension module and everything needed to build it (hopefully in a portable @@ -84,7 +85,7 @@ # When adding arguments to this constructor, be sure to update # setup_keywords in core.py. - def __init__ (self, name, sources, + def __init__(self, name, sources, include_dirs=None, define_macros=None, undef_macros=None, @@ -95,11 +96,11 @@ extra_compile_args=None, extra_link_args=None, export_symbols=None, - swig_opts = None, + swig_opts=None, depends=None, language=None, optional=None, - **kw # To catch unknown keywords + **kw # To catch unknown keywords ): if not isinstance(name, str): raise AssertionError("'name' must be a string") @@ -134,4 +135,3 @@ options = ', '.join(sorted(options)) msg = "Unknown Extension options: %s" % options warnings.warn(msg) - diff --git a/src/distutils2/fancy_getopt.py b/src/distutils2/fancy_getopt.py --- a/src/distutils2/fancy_getopt.py +++ b/src/distutils2/fancy_getopt.py @@ -30,6 +30,7 @@ # (for use as attributes of some object). longopt_xlate = string.maketrans('-', '_') + class FancyGetopt(object): """Wrapper around the standard 'getopt()' module that provides some handy extra functionality: @@ -42,7 +43,7 @@ on the command line sets 'verbose' to false """ - def __init__ (self, option_table=None): + def __init__(self, option_table=None): # The option table is (currently) a list of tuples. The # tuples may have 3 or four values: @@ -180,7 +181,8 @@ self.long_opts.append(long) if long[-1] == '=': # option takes an argument? - if short: short = short + ':' + if short: + short = short + ':' long = long[0:-1] self.takes_arg[long] = 1 else: diff --git a/src/distutils2/manifest.py b/src/distutils2/manifest.py --- a/src/distutils2/manifest.py +++ b/src/distutils2/manifest.py @@ -22,7 +22,7 @@ # a \ followed by some spaces + EOL _COLLAPSE_PATTERN = re.compile('\\\w*\n', re.M) -_COMMENTED_LINE = re.compile('#.*?(?=\n)|^\w*\n|\n(?=$)', re.M|re.S) +_COMMENTED_LINE = re.compile('#.*?(?=\n)|^\w*\n|\n(?=$)', re.M | re.S) class Manifest(object): """A list of files built by on exploring the filesystem and filtered by diff --git a/src/distutils2/metadata.py b/src/distutils2/metadata.py --- a/src/distutils2/metadata.py +++ b/src/distutils2/metadata.py @@ -53,12 +53,12 @@ PKG_INFO_PREFERRED_VERSION = '1.0' _LINE_PREFIX = re.compile('\n \|') -_241_FIELDS = ('Metadata-Version', 'Name', 'Version', 'Platform', +_241_FIELDS = ('Metadata-Version', 'Name', 'Version', 'Platform', 'Summary', 'Description', 'Keywords', 'Home-page', 'Author', 'Author-email', 'License') -_314_FIELDS = ('Metadata-Version', 'Name', 'Version', 'Platform', +_314_FIELDS = ('Metadata-Version', 'Name', 'Version', 'Platform', 'Supported-Platform', 'Summary', 'Description', 'Keywords', 'Home-page', 'Author', 'Author-email', 'License', 'Classifier', 'Download-URL', 'Obsoletes', @@ -66,7 +66,7 @@ _314_MARKERS = ('Obsoletes', 'Provides', 'Requires') -_345_FIELDS = ('Metadata-Version', 'Name', 'Version', 'Platform', +_345_FIELDS = ('Metadata-Version', 'Name', 'Version', 'Platform', 'Supported-Platform', 'Summary', 'Description', 'Keywords', 'Home-page', 'Author', 'Author-email', 'Maintainer', 'Maintainer-email', 'License', diff --git a/src/distutils2/mkpkg.py b/src/distutils2/mkpkg.py --- a/src/distutils2/mkpkg.py +++ b/src/distutils2/mkpkg.py @@ -30,11 +30,15 @@ # # Detect scripts (not sure how. #! outside of package?) -import sys, os, re, shutil, ConfigParser +import sys +import os +import re +import shutil +import ConfigParser helpText = { - 'name' : ''' + 'name': ''' The name of the program to be packaged, usually a single word composed of lower-case characters such as "python", "sqlalchemy", or "CherryPy". ''', @@ -620,28 +624,29 @@ 'Topic :: Utilities', ] -def askYn(question, default = None, helptext = None): +def askYn(question, default=None, helptext=None): while True: - answer = ask(question, default, helptext, required = True) + answer = ask(question, default, helptext, required=True) if answer and answer[0].lower() in 'yn': return(answer[0].lower()) print '\nERROR: You must select "Y" or "N".\n' -def ask(question, default = None, helptext = None, required = True, - lengthy = False, multiline = False): - prompt = '%s: ' % ( question, ) +def ask(question, default=None, helptext=None, required=True, + lengthy=False, multiline=False): + prompt = '%s: ' % (question,) if default: - prompt = '%s [%s]: ' % ( question, default ) + prompt = '%s [%s]: ' % (question, default) if default and len(question) + len(default) > 70: - prompt = '%s\n [%s]: ' % ( question, default ) + prompt = '%s\n [%s]: ' % (question, default) if lengthy or multiline: prompt += '\n >' - if not helptext: helptext = 'No additional help available.' - if helptext[0] == '\n': helptext = helptext[1:] - if helptext[-1] == '\n': helptext = helptext[:-1] + if not helptext: + helptext = 'No additional help available.' + + helptext = helptext.strip("\n") while True: sys.stdout.write(prompt) @@ -653,12 +658,14 @@ print helptext print '=' * 70 continue - if default and not line: return(default) + if default and not line: + return(default) if not line and required: print '*' * 70 print 'This value cannot be empty.' print '===========================' - if helptext: print helptext + if helptext: + print helptext print '*' * 70 continue return(line) @@ -669,7 +676,8 @@ for key in troveList: subDict = dict for subkey in key.split(' :: '): - if not subkey in subDict: subDict[subkey] = {} + if not subkey in subDict: + subDict[subkey] = {} subDict = subDict[subkey] return(dict) troveDict = buildTroveDict(troveList) @@ -687,7 +695,8 @@ def lookupOption(self, key): - if not self.config.has_option('DEFAULT', key): return(None) + if not self.config.has_option('DEFAULT', key): + return(None) return(self.config.get('DEFAULT', key)) @@ -706,7 +715,8 @@ self.config.set('DEFAULT', compareKey, self.setupData[compareKey]) - if not valuesDifferent: return + if not valuesDifferent: + return self.config.write(open(os.path.expanduser('~/.pygiver'), 'w')) @@ -718,7 +728,7 @@ def inspectFile(self, path): fp = open(path, 'r') try: - for line in [ fp.readline() for x in range(10) ]: + for line in [fp.readline() for x in range(10)]: m = re.match(r'^#!.*python((?P\d)(\.\d+)?)?$', line) if m: if m.group('major') == '3': @@ -739,7 +749,8 @@ for root, dirs, files in os.walk('.'): for file in files: - if root == '.' and file == 'setup.py': continue + if root == '.' and file == 'setup.py': + continue fileName = os.path.join(root, file) self.inspectFile(fileName) @@ -761,16 +772,16 @@ self.setupData.get('version'), helpText['version']) self.setupData['description'] = ask('Package description', self.setupData.get('description'), helpText['description'], - lengthy = True) + lengthy=True) self.setupData['author'] = ask('Author name', self.setupData.get('author'), helpText['author']) self.setupData['author_email'] = ask('Author e-mail address', self.setupData.get('author_email'), helpText['author_email']) self.setupData['url'] = ask('Project URL', - self.setupData.get('url'), helpText['url'], required = False) + self.setupData.get('url'), helpText['url'], required=False) if (askYn('Do you want to set Trove classifiers?', - helptext = helpText['do_classifier']) == 'y'): + helptext=helpText['do_classifier']) == 'y'): self.setTroveClassifier() @@ -781,8 +792,10 @@ def setTroveOther(self, classifierDict): - if askYn('Do you want to set other trove identifiers', 'n', - helpText['trove_generic']) != 'y': return + if askYn('Do you want to set other trove identifiers', + 'n', + helpText['trove_generic']) != 'y': + return self.walkTrove(classifierDict, [troveDict], '') @@ -799,7 +812,7 @@ continue if askYn('Do you want to set items under\n "%s" (%d sub-items)' - % ( key, len(trove[key]) ), 'n', + % (key, len(trove[key])), 'n', helpText['trove_generic']) == 'y': self.walkTrove(classifierDict, trovePath + [trove[key]], desc + ' :: ' + key) @@ -808,15 +821,18 @@ def setTroveLicense(self, classifierDict): while True: license = ask('What license do you use', - helptext = helpText['trove_license'], required = False) - if not license: return + helptext=helpText['trove_license'], + required=False) + if not license: + return licenseWords = license.lower().split(' ') foundList = [] for index in range(len(troveList)): troveItem = troveList[index] - if not troveItem.startswith('License :: '): continue + if not troveItem.startswith('License :: '): + continue troveItem = troveItem[11:].lower() allMatch = True @@ -824,17 +840,20 @@ if not word in troveItem: allMatch = False break - if allMatch: foundList.append(index) + if allMatch: + foundList.append(index) question = 'Matching licenses:\n\n' for i in xrange(1, len(foundList) + 1): - question += ' %s) %s\n' % ( i, troveList[foundList[i - 1]] ) + question += ' %s) %s\n' % (i, troveList[foundList[i - 1]]) question += ('\nType the number of the license you wish to use or ' '? to try again:') - troveLicense = ask(question, required = False) + troveLicense = ask(question, required=False) - if troveLicense == '?': continue - if troveLicense == '': return + if troveLicense == '?': + continue + if troveLicense == '': + return foundIndex = foundList[int(troveLicense) - 1] classifierDict[troveList[foundIndex]] = 1 try: @@ -856,7 +875,7 @@ 6 - Mature 7 - Inactive -Status''', required = False) +Status''', required=False) if devStatus: try: key = { @@ -884,7 +903,8 @@ return modified_pkgs def writeSetup(self): - if os.path.exists('setup.py'): shutil.move('setup.py', 'setup.py.old') + if os.path.exists('setup.py'): + shutil.move('setup.py', 'setup.py.old') fp = open('setup.py', 'w') try: -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:47 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:47 +0200 Subject: [Python-checkins] distutils2: Automated merge with ssh://hg@bitbucket.org/mtlpython/distutils2 Message-ID: tarek.ziade pushed e14f2d4cee22 to distutils2: http://hg.python.org/distutils2/rev/e14f2d4cee22 changeset: 490:e14f2d4cee22 parent: 488:475e8d8deddf parent: 486:2a5ed12ac6db user: Yannick Gingras date: Fri Aug 06 00:02:02 2010 -0400 summary: Automated merge with ssh://hg at bitbucket.org/mtlpython/distutils2 files: diff --git a/src/runtests-cov.py b/src/runtests-cov.py --- a/src/runtests-cov.py +++ b/src/runtests-cov.py @@ -5,16 +5,18 @@ """ import sys -from os.path import dirname, islink, realpath +from os.path import dirname, islink, realpath, join, abspath from optparse import OptionParser +COVERAGE_FILE = join(dirname(abspath(__file__)), '.coverage') + def get_coverage(): """ Return a usable coverage object. """ # deferred import because coverage is optional import coverage cov = getattr(coverage, "the_coverage", None) if not cov: - cov = coverage.coverage() + cov = coverage.coverage(COVERAGE_FILE) return cov def ignore_prefixes(module): @@ -61,6 +63,7 @@ else: morfs = "distutils2/*" # running coverage 2.x + cov.cache = COVERAGE_FILE cov.restore() prefixes = ["runtests", "distutils2/tests", "distutils2/_backport"] -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:47 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:47 +0200 Subject: [Python-checkins] distutils2: Remove obsolete comment Message-ID: tarek.ziade pushed bce5ffe93848 to distutils2: http://hg.python.org/distutils2/rev/bce5ffe93848 changeset: 489:bce5ffe93848 parent: 480:06da7c8c1f66 user: ?ric Araujo date: Fri Aug 06 05:39:05 2010 +0200 summary: Remove obsolete comment files: src/distutils2/dist.py diff --git a/src/distutils2/dist.py b/src/distutils2/dist.py --- a/src/distutils2/dist.py +++ b/src/distutils2/dist.py @@ -1004,13 +1004,8 @@ not self.has_ext_modules() and not self.has_c_libraries()) - # -- Metadata query methods ---------------------------------------- - # If you're looking for 'get_name()', 'get_version()', and so forth, - # they are defined in a sneaky way: the constructor binds self.get_XXX - # to self.metadata.get_XXX. The actual code is in the - # DistributionMetadata class, below. - +# XXX keep for compat or remove? def fix_help_options(options): """Convert a 4-tuple 'help_options' list as found in various command -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:47 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:47 +0200 Subject: [Python-checkins] distutils2: Branch merge Message-ID: tarek.ziade pushed 21e341cd6b11 to distutils2: http://hg.python.org/distutils2/rev/21e341cd6b11 changeset: 492:21e341cd6b11 parent: 491:0c740ea7855f parent: 480:06da7c8c1f66 user: ?ric Araujo date: Fri Aug 06 06:17:22 2010 +0200 summary: Branch merge files: diff --git a/src/DEVNOTES.txt b/src/DEVNOTES.txt --- a/src/DEVNOTES.txt +++ b/src/DEVNOTES.txt @@ -17,6 +17,5 @@ - DistributionMetadata > Metadata or ReleaseMetadata - pkgutil > pkgutil.__init__ + new pkgutil.database (or better name) - - pypi > index - RationalizedVersion > Version - suggest_rationalized_version > suggest -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:47 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:47 +0200 Subject: [Python-checkins] distutils2: made the list of tested modules exhaustive when using coverage 2.x Message-ID: tarek.ziade pushed 0c740ea7855f to distutils2: http://hg.python.org/distutils2/rev/0c740ea7855f changeset: 491:0c740ea7855f user: Yannick Gingras date: Fri Aug 06 00:15:08 2010 -0400 summary: made the list of tested modules exhaustive when using coverage 2.x files: src/runtests-cov.py diff --git a/src/runtests-cov.py b/src/runtests-cov.py --- a/src/runtests-cov.py +++ b/src/runtests-cov.py @@ -61,10 +61,10 @@ cov.load() morfs = None else: - morfs = "distutils2/*" # running coverage 2.x cov.cache = COVERAGE_FILE cov.restore() + morfs = [m for m in cov.cexecuted.keys() if "distutils2" in m] prefixes = ["runtests", "distutils2/tests", "distutils2/_backport"] prefixes += ignore_prefixes(unittest) -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:47 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:47 +0200 Subject: [Python-checkins] distutils2: Branch merge Message-ID: tarek.ziade pushed d5aa0034f8ed to distutils2: http://hg.python.org/distutils2/rev/d5aa0034f8ed changeset: 493:d5aa0034f8ed parent: 489:bce5ffe93848 parent: 492:21e341cd6b11 user: ?ric Araujo date: Fri Aug 06 06:18:02 2010 +0200 summary: Branch merge files: src/distutils2/dist.py diff --git a/src/distutils2/__init__.py b/src/distutils2/__init__.py --- a/src/distutils2/__init__.py +++ b/src/distutils2/__init__.py @@ -20,7 +20,7 @@ __version__ = "1.0a2" -# when set to True, converts doctests by default too +# when set to True, converts doctests by default too run_2to3_on_doctests = True # Standard package names for fixer packages lib2to3_fixer_packages = ['lib2to3.fixes'] diff --git a/src/distutils2/core.py b/src/distutils2/core.py --- a/src/distutils2/core.py +++ b/src/distutils2/core.py @@ -33,6 +33,7 @@ or: %(script)s cmd --help """ + def gen_usage(script_name): script = os.path.basename(script_name) return USAGE % {'script': script} @@ -59,6 +60,7 @@ 'extra_objects', 'extra_compile_args', 'extra_link_args', 'swig_opts', 'export_symbols', 'depends', 'language') + def setup(**attrs): """The gateway to the Distutils: do everything your setup script needs to do, in a highly flexible and user-driven way. Briefly: create a diff --git a/src/distutils2/depgraph.py b/src/distutils2/depgraph.py --- a/src/distutils2/depgraph.py +++ b/src/distutils2/depgraph.py @@ -1,5 +1,5 @@ -"""Analyse the relationships between the distributions in the system and generate -a dependency graph. +"""Analyse the relationships between the distributions in the system +and generate a dependency graph. """ from distutils2.errors import DistutilsError diff --git a/src/distutils2/dist.py b/src/distutils2/dist.py --- a/src/distutils2/dist.py +++ b/src/distutils2/dist.py @@ -6,12 +6,10 @@ __revision__ = "$Id: dist.py 77717 2010-01-24 00:33:32Z tarek.ziade $" -import sys, os, re - -try: - import warnings -except ImportError: - warnings = None +import sys +import os +import re +import warnings from ConfigParser import RawConfigParser @@ -26,7 +24,8 @@ # the same as a Python NAME -- I don't allow leading underscores. The fact # that they're very similar is no coincidence; the default naming scheme is # to look for a Python module named after the command. -command_re = re.compile (r'^[a-zA-Z]([a-zA-Z0-9_]*)$') +command_re = re.compile(r'^[a-zA-Z]([a-zA-Z0-9_]*)$') + class Distribution(object): """The core of the Distutils. Most of the work hiding behind 'setup' @@ -123,10 +122,8 @@ # negative options are options that exclude other options negative_opt = {'quiet': 'verbose'} - # -- Creation/initialization methods ------------------------------- - - def __init__ (self, attrs=None): + def __init__(self, attrs=None): """Construct a new Distribution instance: initialize all the attributes of a Distribution, and then use 'attrs' (a dictionary mapping attribute names to values) to assign some of those @@ -251,10 +248,7 @@ setattr(self, key, val) else: msg = "Unknown distribution option: %r" % key - if warnings is not None: - warnings.warn(msg) - else: - sys.stderr.write(msg + "\n") + warnings.warn(msg) # no-user-cfg is handled before other command line args # because other args override the config files, and this @@ -565,10 +559,10 @@ if (hasattr(cmd_class, 'help_options') and isinstance(cmd_class.help_options, list)): - help_option_found=0 + help_option_found = 0 for (help_option, short, desc, func) in cmd_class.help_options: if hasattr(opts, parser.get_attr_name(help_option)): - help_option_found=1 + help_option_found = 1 if hasattr(func, '__call__'): func() else: @@ -807,7 +801,7 @@ class_name = command try: - __import__ (module_name) + __import__(module_name) module = sys.modules[module_name] except ImportError: continue @@ -824,7 +818,6 @@ raise DistutilsModuleError("invalid command '%s'" % command) - def get_command_obj(self, command, create=1): """Return the command object for 'command'. Normally this object is cached on a previous call to 'get_command_obj()'; if no command @@ -967,7 +960,6 @@ self.run_command_hooks(cmd_obj, 'post_hook') self.have_run[command] = 1 - def run_command_hooks(self, cmd_obj, hook_kind): hooks = getattr(cmd_obj, hook_kind) if hooks is None: @@ -977,7 +969,6 @@ hook_func(cmd_obj) # -- Distribution query methods ------------------------------------ - def has_pure_modules(self): return len(self.packages or self.py_modules or []) > 0 diff --git a/src/distutils2/errors.py b/src/distutils2/errors.py --- a/src/distutils2/errors.py +++ b/src/distutils2/errors.py @@ -10,31 +10,38 @@ __revision__ = "$Id: errors.py 75901 2009-10-28 06:45:18Z tarek.ziade $" + class DistutilsError(Exception): """The root of all Distutils evil.""" + class DistutilsModuleError(DistutilsError): """Unable to load an expected module, or to find an expected class within some module (in particular, command modules and classes).""" + class DistutilsClassError(DistutilsError): """Some command class (or possibly distribution class, if anyone feels a need to subclass Distribution) is found not to be holding up its end of the bargain, ie. implementing some part of the "command "interface.""" + class DistutilsGetoptError(DistutilsError): """The option table provided to 'fancy_getopt()' is bogus.""" + class DistutilsArgError(DistutilsError): """Raised by fancy_getopt in response to getopt.error -- ie. an error in the command line usage.""" + class DistutilsFileError(DistutilsError): """Any problems in the filesystem: expected file not found, etc. Typically this is for problems that we detect before IOError or OSError could be raised.""" + class DistutilsOptionError(DistutilsError): """Syntactic/semantic errors in command options, such as use of mutually conflicting options, or inconsistent options, @@ -43,60 +50,76 @@ files, or what-have-you -- but if we *know* something originated in the setup script, we'll raise DistutilsSetupError instead.""" + class DistutilsSetupError(DistutilsError): """For errors that can be definitely blamed on the setup script, such as invalid keyword arguments to 'setup()'.""" + class DistutilsPlatformError(DistutilsError): """We don't know how to do something on the current platform (but we do know how to do it on some platform) -- eg. trying to compile C files on a platform not supported by a CCompiler subclass.""" + class DistutilsExecError(DistutilsError): """Any problems executing an external program (such as the C compiler, when compiling C files).""" + class DistutilsInternalError(DistutilsError): """Internal inconsistencies or impossibilities (obviously, this should never be seen if the code is working!).""" + class DistutilsTemplateError(DistutilsError): """Syntax error in a file list template.""" + class DistutilsByteCompileError(DistutilsError): """Byte compile error.""" + # Exception classes used by the CCompiler implementation classes class CCompilerError(Exception): """Some compile/link operation failed.""" + class PreprocessError(CCompilerError): """Failure to preprocess one or more C/C++ files.""" + class CompileError(CCompilerError): """Failure to compile one or more C/C++ source files.""" + class LibError(CCompilerError): """Failure to create a static library from one or more C/C++ object files.""" + class LinkError(CCompilerError): """Failure to link one or more C/C++ object files into an executable or shared library file.""" + class UnknownFileError(CCompilerError): """Attempt to process an unknown file type.""" + class MetadataConflictError(DistutilsError): """Attempt to read or write metadata fields that are conflictual.""" + class MetadataUnrecognizedVersionError(DistutilsError): """Unknown metadata version number.""" + class IrrationalVersionError(Exception): """This is an irrational version.""" pass + class HugeMajorVersionNumError(IrrationalVersionError): """An irrational version because the major version number is huge (often because a year or date was used). @@ -105,4 +128,3 @@ This guard can be disabled by setting that option False. """ pass - diff --git a/src/distutils2/extension.py b/src/distutils2/extension.py --- a/src/distutils2/extension.py +++ b/src/distutils2/extension.py @@ -17,6 +17,7 @@ # import that large-ish module (indirectly, through distutils.core) in # order to do anything. + class Extension(object): """Just a collection of attributes that describes an extension module and everything needed to build it (hopefully in a portable @@ -84,7 +85,7 @@ # When adding arguments to this constructor, be sure to update # setup_keywords in core.py. - def __init__ (self, name, sources, + def __init__(self, name, sources, include_dirs=None, define_macros=None, undef_macros=None, @@ -95,11 +96,11 @@ extra_compile_args=None, extra_link_args=None, export_symbols=None, - swig_opts = None, + swig_opts=None, depends=None, language=None, optional=None, - **kw # To catch unknown keywords + **kw # To catch unknown keywords ): if not isinstance(name, str): raise AssertionError("'name' must be a string") @@ -134,4 +135,3 @@ options = ', '.join(sorted(options)) msg = "Unknown Extension options: %s" % options warnings.warn(msg) - diff --git a/src/distutils2/fancy_getopt.py b/src/distutils2/fancy_getopt.py --- a/src/distutils2/fancy_getopt.py +++ b/src/distutils2/fancy_getopt.py @@ -30,6 +30,7 @@ # (for use as attributes of some object). longopt_xlate = string.maketrans('-', '_') + class FancyGetopt(object): """Wrapper around the standard 'getopt()' module that provides some handy extra functionality: @@ -42,7 +43,7 @@ on the command line sets 'verbose' to false """ - def __init__ (self, option_table=None): + def __init__(self, option_table=None): # The option table is (currently) a list of tuples. The # tuples may have 3 or four values: @@ -180,7 +181,8 @@ self.long_opts.append(long) if long[-1] == '=': # option takes an argument? - if short: short = short + ':' + if short: + short = short + ':' long = long[0:-1] self.takes_arg[long] = 1 else: diff --git a/src/distutils2/manifest.py b/src/distutils2/manifest.py --- a/src/distutils2/manifest.py +++ b/src/distutils2/manifest.py @@ -22,7 +22,7 @@ # a \ followed by some spaces + EOL _COLLAPSE_PATTERN = re.compile('\\\w*\n', re.M) -_COMMENTED_LINE = re.compile('#.*?(?=\n)|^\w*\n|\n(?=$)', re.M|re.S) +_COMMENTED_LINE = re.compile('#.*?(?=\n)|^\w*\n|\n(?=$)', re.M | re.S) class Manifest(object): """A list of files built by on exploring the filesystem and filtered by diff --git a/src/distutils2/metadata.py b/src/distutils2/metadata.py --- a/src/distutils2/metadata.py +++ b/src/distutils2/metadata.py @@ -52,12 +52,12 @@ PKG_INFO_PREFERRED_VERSION = '1.0' _LINE_PREFIX = re.compile('\n \|') -_241_FIELDS = ('Metadata-Version', 'Name', 'Version', 'Platform', +_241_FIELDS = ('Metadata-Version', 'Name', 'Version', 'Platform', 'Summary', 'Description', 'Keywords', 'Home-page', 'Author', 'Author-email', 'License') -_314_FIELDS = ('Metadata-Version', 'Name', 'Version', 'Platform', +_314_FIELDS = ('Metadata-Version', 'Name', 'Version', 'Platform', 'Supported-Platform', 'Summary', 'Description', 'Keywords', 'Home-page', 'Author', 'Author-email', 'License', 'Classifier', 'Download-URL', 'Obsoletes', @@ -65,7 +65,7 @@ _314_MARKERS = ('Obsoletes', 'Provides', 'Requires') -_345_FIELDS = ('Metadata-Version', 'Name', 'Version', 'Platform', +_345_FIELDS = ('Metadata-Version', 'Name', 'Version', 'Platform', 'Supported-Platform', 'Summary', 'Description', 'Keywords', 'Home-page', 'Author', 'Author-email', 'Maintainer', 'Maintainer-email', 'License', diff --git a/src/distutils2/mkpkg.py b/src/distutils2/mkpkg.py --- a/src/distutils2/mkpkg.py +++ b/src/distutils2/mkpkg.py @@ -30,7 +30,11 @@ # # Detect scripts (not sure how. #! outside of package?) -import sys, os, re, shutil, ConfigParser +import sys +import os +import re +import shutil +import ConfigParser helpText = { @@ -629,19 +633,20 @@ print '\nERROR: You must select "Y" or "N".\n' -def ask(question, default = None, helptext = None, required = True, - lengthy = False, multiline = False): - prompt = '%s: ' % ( question, ) +def ask(question, default=None, helptext=None, required=True, + lengthy=False, multiline=False): + prompt = '%s: ' % (question,) if default: - prompt = '%s [%s]: ' % ( question, default ) + prompt = '%s [%s]: ' % (question, default) if default and len(question) + len(default) > 70: - prompt = '%s\n [%s]: ' % ( question, default ) + prompt = '%s\n [%s]: ' % (question, default) if lengthy or multiline: prompt += '\n >' - if not helptext: helptext = 'No additional help available.' - if helptext[0] == '\n': helptext = helptext[1:] - if helptext[-1] == '\n': helptext = helptext[:-1] + if not helptext: + helptext = 'No additional help available.' + + helptext = helptext.strip("\n") while True: sys.stdout.write(prompt) @@ -653,12 +658,14 @@ print helptext print '=' * 70 continue - if default and not line: return(default) + if default and not line: + return(default) if not line and required: print '*' * 70 print 'This value cannot be empty.' print '===========================' - if helptext: print helptext + if helptext: + print helptext print '*' * 70 continue return(line) @@ -669,7 +676,8 @@ for key in troveList: subDict = dict for subkey in key.split(' :: '): - if not subkey in subDict: subDict[subkey] = {} + if not subkey in subDict: + subDict[subkey] = {} subDict = subDict[subkey] return(dict) troveDict = buildTroveDict(troveList) @@ -687,7 +695,8 @@ def lookupOption(self, key): - if not self.config.has_option('DEFAULT', key): return(None) + if not self.config.has_option('DEFAULT', key): + return(None) return(self.config.get('DEFAULT', key)) @@ -706,7 +715,8 @@ self.config.set('DEFAULT', compareKey, self.setupData[compareKey]) - if not valuesDifferent: return + if not valuesDifferent: + return self.config.write(open(os.path.expanduser('~/.pygiver'), 'w')) @@ -718,7 +728,7 @@ def inspectFile(self, path): fp = open(path, 'r') try: - for line in [ fp.readline() for x in range(10) ]: + for line in [fp.readline() for x in range(10)]: m = re.match(r'^#!.*python((?P\d)(\.\d+)?)?$', line) if m: if m.group('major') == '3': @@ -761,16 +771,16 @@ self.setupData.get('version'), helpText['version']) self.setupData['description'] = ask('Package description', self.setupData.get('description'), helpText['description'], - lengthy = True) + lengthy=True) self.setupData['author'] = ask('Author name', self.setupData.get('author'), helpText['author']) self.setupData['author_email'] = ask('Author e-mail address', self.setupData.get('author_email'), helpText['author_email']) self.setupData['url'] = ask('Project URL', - self.setupData.get('url'), helpText['url'], required = False) + self.setupData.get('url'), helpText['url'], required=False) if (askYn('Do you want to set Trove classifiers?', - helptext = helpText['do_classifier']) == 'y'): + helptext=helpText['do_classifier']) == 'y'): self.setTroveClassifier() @@ -781,8 +791,10 @@ def setTroveOther(self, classifierDict): - if askYn('Do you want to set other trove identifiers', 'n', - helpText['trove_generic']) != 'y': return + if askYn('Do you want to set other trove identifiers', + 'n', + helpText['trove_generic']) != 'y': + return self.walkTrove(classifierDict, [troveDict], '') @@ -799,7 +811,7 @@ continue if askYn('Do you want to set items under\n "%s" (%d sub-items)' - % ( key, len(trove[key]) ), 'n', + % (key, len(trove[key])), 'n', helpText['trove_generic']) == 'y': self.walkTrove(classifierDict, trovePath + [trove[key]], desc + ' :: ' + key) @@ -808,15 +820,18 @@ def setTroveLicense(self, classifierDict): while True: license = ask('What license do you use', - helptext = helpText['trove_license'], required = False) - if not license: return + helptext=helpText['trove_license'], + required=False) + if not license: + return licenseWords = license.lower().split(' ') foundList = [] for index in range(len(troveList)): troveItem = troveList[index] - if not troveItem.startswith('License :: '): continue + if not troveItem.startswith('License :: '): + continue troveItem = troveItem[11:].lower() allMatch = True @@ -824,17 +839,20 @@ if not word in troveItem: allMatch = False break - if allMatch: foundList.append(index) + if allMatch: + foundList.append(index) question = 'Matching licenses:\n\n' for i in xrange(1, len(foundList) + 1): - question += ' %s) %s\n' % ( i, troveList[foundList[i - 1]] ) + question += ' %s) %s\n' % (i, troveList[foundList[i - 1]]) question += ('\nType the number of the license you wish to use or ' '? to try again:') - troveLicense = ask(question, required = False) + troveLicense = ask(question, required=False) - if troveLicense == '?': continue - if troveLicense == '': return + if troveLicense == '?': + continue + if troveLicense == '': + return foundIndex = foundList[int(troveLicense) - 1] classifierDict[troveList[foundIndex]] = 1 try: @@ -856,7 +874,7 @@ 6 - Mature 7 - Inactive -Status''', required = False) +Status''', required=False) if devStatus: try: key = { @@ -884,7 +902,8 @@ return modified_pkgs def writeSetup(self): - if os.path.exists('setup.py'): shutil.move('setup.py', 'setup.py.old') + if os.path.exists('setup.py'): + shutil.move('setup.py', 'setup.py.old') fp = open('setup.py', 'w') try: diff --git a/src/distutils2/tests/test_dist.py b/src/distutils2/tests/test_dist.py --- a/src/distutils2/tests/test_dist.py +++ b/src/distutils2/tests/test_dist.py @@ -153,6 +153,27 @@ my_file2 = os.path.join(tmp_dir, 'f2') dist.metadata.write_file(open(my_file, 'w')) + def test_bad_attr(self): + cls = Distribution + + # catching warnings + warns = [] + def _warn(msg): + warns.append(msg) + + old_warn = warnings.warn + warnings.warn = _warn + try: + dist = cls(attrs={'author': 'xxx', + 'name': 'xxx', + 'version': 'xxx', + 'url': 'xxxx', + 'badoptname': 'xxx'}) + finally: + warnings.warn = old_warn + + self.assertTrue(len(warns)==1 and "Unknown distribution" in warns[0]) + def test_empty_options(self): # an empty options dictionary should not stay in the # list of attributes @@ -176,6 +197,21 @@ self.assertEqual(len(warns), 0) + def test_non_empty_options(self): + # TODO: how to actually use options is not documented except + # for a few cryptic comments in dist.py. If this is to stay + # in the public API, it deserves some better documentation. + + # Here is an example of how it's used out there: + # http://svn.pythonmac.org/py2app/py2app/trunk/doc/index.html#specifying-customizations + cls = Distribution + dist = cls(attrs={'author': 'xxx', + 'name': 'xxx', + 'version': 'xxx', + 'url': 'xxxx', + 'options': dict(sdist=dict(owner="root"))}) + self.assertTrue("owner" in dist.get_option_dict("sdist")) + def test_finalize_options(self): attrs = {'keywords': 'one,two', diff --git a/src/runtests-cov.py b/src/runtests-cov.py --- a/src/runtests-cov.py +++ b/src/runtests-cov.py @@ -5,9 +5,19 @@ """ import sys -from os.path import dirname, islink, realpath +from os.path import dirname, islink, realpath, join, abspath from optparse import OptionParser +COVERAGE_FILE = join(dirname(abspath(__file__)), '.coverage') + +def get_coverage(): + """ Return a usable coverage object. """ + # deferred import because coverage is optional + import coverage + cov = getattr(coverage, "the_coverage", None) + if not cov: + cov = coverage.coverage(COVERAGE_FILE) + return cov def ignore_prefixes(module): """ Return a list of prefixes to ignore in the coverage report if @@ -44,10 +54,17 @@ def coverage_report(opts): - import coverage from distutils2.tests.support import unittest - cov = coverage.coverage() - cov.load() + cov = get_coverage() + if hasattr(cov, "load"): + # running coverage 3.x + cov.load() + morfs = None + else: + # running coverage 2.x + cov.cache = COVERAGE_FILE + cov.restore() + morfs = [m for m in cov.cexecuted.keys() if "distutils2" in m] prefixes = ["runtests", "distutils2/tests", "distutils2/_backport"] prefixes += ignore_prefixes(unittest) @@ -66,7 +83,7 @@ # that module is also completely optional pass - cov.report(omit_prefixes=prefixes, show_missing=opts.show_missing) + cov.report(morfs, omit_prefixes=prefixes, show_missing=opts.show_missing) def test_main(): @@ -74,11 +91,8 @@ verbose = not opts.quiet ret = 0 - if opts.coverage or opts.report: - import coverage - if opts.coverage: - cov = coverage.coverage() + cov = get_coverage() cov.erase() cov.start() if not opts.report: -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:47 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:47 +0200 Subject: [Python-checkins] distutils2: Use VersionPredicate to get the name in depgraph instead of parsing it twice. Message-ID: tarek.ziade pushed 3c734f59ebf2 to distutils2: http://hg.python.org/distutils2/rev/3c734f59ebf2 changeset: 495:3c734f59ebf2 parent: 474:437bda319048 user: Alexis Metaireau date: Fri Aug 06 16:43:26 2010 +0200 summary: Use VersionPredicate to get the name in depgraph instead of parsing it twice. files: src/distutils2/depgraph.py diff --git a/src/distutils2/depgraph.py b/src/distutils2/depgraph.py --- a/src/distutils2/depgraph.py +++ b/src/distutils2/depgraph.py @@ -135,8 +135,7 @@ requires = dist.metadata['Requires-Dist'] + dist.metadata['Requires'] for req in requires: predicate = VersionPredicate(req) - comps = req.strip().rsplit(" ", 1) - name = comps[0] + name = predicate.name if not name in provided: graph.add_missing(dist, req) -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:47 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:47 +0200 Subject: [Python-checkins] distutils2: Add a way for a version predicate to get back the predicate as a string. Message-ID: tarek.ziade pushed 57fc2d307990 to distutils2: http://hg.python.org/distutils2/rev/57fc2d307990 changeset: 497:57fc2d307990 user: Alexis Metaireau date: Fri Aug 06 16:51:31 2010 +0200 summary: Add a way for a version predicate to get back the predicate as a string. files: src/distutils2/version.py diff --git a/src/distutils2/version.py b/src/distutils2/version.py --- a/src/distutils2/version.py +++ b/src/distutils2/version.py @@ -349,6 +349,7 @@ } def __init__(self, predicate): + self._string = predicate predicate = predicate.strip() match = _PREDICATE.match(predicate) if match is None: @@ -374,6 +375,9 @@ return False return True + def __repr__(self): + return self._string + class _Versions(VersionPredicate): def __init__(self, predicate): -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:47 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:47 +0200 Subject: [Python-checkins] distutils2: Minor changes: rename predicate to requirements, add some documentation and Message-ID: tarek.ziade pushed 77de6a8b337a to distutils2: http://hg.python.org/distutils2/rev/77de6a8b337a changeset: 502:77de6a8b337a user: Alexis Metaireau date: Fri Aug 06 17:13:25 2010 +0200 summary: Minor changes: rename predicate to requirements, add some documentation and files: src/distutils2/index/dist.py, src/distutils2/index/xmlrpc.py diff --git a/src/distutils2/index/dist.py b/src/distutils2/index/dist.py --- a/src/distutils2/index/dist.py +++ b/src/distutils2/index/dist.py @@ -39,6 +39,7 @@ class IndexReference(object): + """Mixin used to store the index reference""" def set_index(self, index=None): self._index = index @@ -367,7 +368,7 @@ if predicate.match(release.version)], index=self._index) - def get_last(self, predicate, prefer_final=None): + def get_last(self, requirements, prefer_final=None): """Return the "last" release, that satisfy the given predicates. "last" is defined by the version number of the releases, you also could diff --git a/src/distutils2/index/xmlrpc.py b/src/distutils2/index/xmlrpc.py --- a/src/distutils2/index/xmlrpc.py +++ b/src/distutils2/index/xmlrpc.py @@ -94,7 +94,7 @@ if not versions: raise ProjectNotFound(project_name) project = self._get_project(project_name) - project.add_releases([ReleaseInfo(project_name, version, + project.add_releases([ReleaseInfo(project_name, version, index=self._index) for version in versions]) project = project.filter(predicate) @@ -102,7 +102,7 @@ raise ReleaseNotFound("%s" % predicate) project.sort_releases(prefer_final) return project - + def get_distributions(self, project_name, version): """Grab informations about distributions from XML-RPC. @@ -121,7 +121,7 @@ dist_infos = {'url': info['url'], 'hashval': info['md5_digest'], 'hashname': 'md5', - 'is_external': False, + 'is_external': False, 'python_version': info['python_version']} release.add_distribution(packagetype, **dist_infos) return release -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:47 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:47 +0200 Subject: [Python-checkins] distutils2: Move get_version_predicate to version (instead of index.base) Message-ID: tarek.ziade pushed b369e38e3fc3 to distutils2: http://hg.python.org/distutils2/rev/b369e38e3fc3 changeset: 496:b369e38e3fc3 user: Alexis Metaireau date: Fri Aug 06 16:48:33 2010 +0200 summary: Move get_version_predicate to version (instead of index.base) files: src/distutils2/index/base.py, src/distutils2/index/dist.py, src/distutils2/index/simple.py, src/distutils2/index/xmlrpc.py, src/distutils2/version.py diff --git a/src/distutils2/index/base.py b/src/distutils2/index/base.py --- a/src/distutils2/index/base.py +++ b/src/distutils2/index/base.py @@ -1,4 +1,3 @@ -from distutils2.version import VersionPredicate from distutils2.index.dist import ReleasesList @@ -10,14 +9,6 @@ self._prefer_source = prefer_source self._index = self - def _get_version_predicate(self, requirements): - """Return a VersionPredicate object, from a string or an already - existing object. - """ - if isinstance(requirements, str): - requirements = VersionPredicate(requirements) - return requirements - def _get_prefer_final(self, prefer_final=None): """Return the prefer_final internal parameter or the specified one if provided""" diff --git a/src/distutils2/index/dist.py b/src/distutils2/index/dist.py --- a/src/distutils2/index/dist.py +++ b/src/distutils2/index/dist.py @@ -26,7 +26,8 @@ from distutils2.errors import IrrationalVersionError from distutils2.index.errors import (HashDoesNotMatch, UnsupportedHashName, CantParseArchiveName) -from distutils2.version import suggest_normalized_version, NormalizedVersion +from distutils2.version import (suggest_normalized_version, NormalizedVersion, + get_version_predicate) from distutils2.metadata import DistributionMetadata from distutils2.util import untar_file, unzip_file, splitext @@ -380,6 +381,7 @@ "last" is defined by the version number of the releases, you also could set prefer_final parameter to True or False to change the order results """ + predicate = get_version_predicate(requirements) releases = self.filter(predicate) releases.sort_releases(prefer_final, reverse=True) return releases[0] diff --git a/src/distutils2/index/simple.py b/src/distutils2/index/simple.py --- a/src/distutils2/index/simple.py +++ b/src/distutils2/index/simple.py @@ -22,6 +22,7 @@ ReleaseNotFound, ProjectNotFound) from distutils2.index.mirrors import get_mirrors from distutils2.metadata import DistributionMetadata +from distutils2.version import get_version_predicate from distutils2 import __version__ as __distutils2_version__ __all__ = ['Crawler', 'DEFAULT_SIMPLE_INDEX_URL'] @@ -158,7 +159,7 @@ """Search for releases and return a ReleaseList object containing the results. """ - predicate = self._get_version_predicate(requirements) + predicate = get_version_predicate(requirements) if self._projects.has_key(predicate.name.lower()) and not force_update: return self._projects.get(predicate.name.lower()) prefer_final = self._get_prefer_final(prefer_final) @@ -173,7 +174,7 @@ def get_release(self, requirements, prefer_final=None): """Return only one release that fulfill the given requirements""" - predicate = self._get_version_predicate(requirements) + predicate = get_version_predicate(requirements) release = self.get_releases(predicate, prefer_final)\ .get_last(predicate) if not release: diff --git a/src/distutils2/index/xmlrpc.py b/src/distutils2/index/xmlrpc.py --- a/src/distutils2/index/xmlrpc.py +++ b/src/distutils2/index/xmlrpc.py @@ -5,6 +5,7 @@ from distutils2.index.base import BaseClient from distutils2.index.errors import ProjectNotFound, InvalidSearchField from distutils2.index.dist import ReleaseInfo +from distutils2.version import get_version_predicate __all__ = ['Client', 'DEFAULT_XMLRPC_INDEX_URL'] @@ -41,7 +42,7 @@ related informations. """ prefer_final = self._get_prefer_final(prefer_final) - predicate = self._get_version_predicate(requirements) + predicate = get_version_predicate(requirements) releases = self.get_releases(predicate.name) release = releases.get_last(predicate, prefer_final) self.get_metadata(release.name, "%s" % release.version) @@ -72,7 +73,7 @@ def get_versions(project_name, show_hidden): return self.proxy.package_releases(project_name, show_hidden) - predicate = self._get_version_predicate(requirements) + predicate = get_version_predicate(requirements) prefer_final = self._get_prefer_final(prefer_final) project_name = predicate.name if not force_update and (project_name.lower() in self._projects): diff --git a/src/distutils2/version.py b/src/distutils2/version.py --- a/src/distutils2/version.py +++ b/src/distutils2/version.py @@ -418,3 +418,12 @@ return False else: return True + + +def get_version_predicate(requirements): + """Return a VersionPredicate object, from a string or an already + existing object. + """ + if isinstance(requirements, str): + requirements = VersionPredicate(requirements) + return requirements -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:47 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:47 +0200 Subject: [Python-checkins] distutils2: Add more verbose warnings while parsing wrong versions/predicates. Message-ID: tarek.ziade pushed 127b16b64404 to distutils2: http://hg.python.org/distutils2/rev/127b16b64404 changeset: 498:127b16b64404 user: Alexis Metaireau date: Fri Aug 06 16:53:02 2010 +0200 summary: Add more verbose warnings while parsing wrong versions/predicates. files: src/distutils2/metadata.py diff --git a/src/distutils2/metadata.py b/src/distutils2/metadata.py --- a/src/distutils2/metadata.py +++ b/src/distutils2/metadata.py @@ -337,7 +337,7 @@ def update(self, other=None, **kwargs): """Set metadata values from the given mapping - + Convert the keys to Metadata fields. Given keys that don't match a metadata argument will not be used. @@ -350,7 +350,7 @@ Empty values (e.g. None and []) are not setted this way. """ def _set(key, value): - if value not in ([], None) and key in _ATTR2FIELD: + if value not in ([], None, '') and key in _ATTR2FIELD: self.set(self._convert_name(key), value) if other is None: @@ -388,13 +388,16 @@ for v in value: # check that the values are valid predicates if not is_valid_predicate(v.split(';')[0]): - warn('"%s" is not a valid predicate' % v) + warn('"%s" is not a valid predicate (field "%s")' % + (v, name)) elif name in _VERSIONS_FIELDS and value is not None: if not is_valid_versions(value): - warn('"%s" is not a valid predicate' % value) + warn('"%s" is not a valid version (field "%s")' % + (value, name)) elif name in _VERSION_FIELDS and value is not None: if not is_valid_version(value): - warn('"%s" is not a valid version' % value) + warn('"%s" is not a valid version (field "%s")' % + (value, name)) if name in _UNICODEFIELDS: value = self._encode_field(value) -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:47 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:47 +0200 Subject: [Python-checkins] distutils2: Document a bit the sources of the pypi mock server. Message-ID: tarek.ziade pushed 812fd0cfd1a2 to distutils2: http://hg.python.org/distutils2/rev/812fd0cfd1a2 changeset: 499:812fd0cfd1a2 user: Alexis Metaireau date: Fri Aug 06 17:05:59 2010 +0200 summary: Document a bit the sources of the pypi mock server. files: src/distutils2/tests/pypi_server.py diff --git a/src/distutils2/tests/pypi_server.py b/src/distutils2/tests/pypi_server.py --- a/src/distutils2/tests/pypi_server.py +++ b/src/distutils2/tests/pypi_server.py @@ -3,6 +3,30 @@ This module also provides a simple test case to extend if you need to use the PyPIServer all along your test case. Be sure to read the documentation before any use. + +XXX TODO: + +The mock server can handle simple HTTP request (to simulate a simple index) or +XMLRPC requests, over HTTP. Both does not have the same intergface to deal +with, and I think it's a pain. + +A good idea could be to re-think a bit the way dstributions are handled in the +mock server. As it should return malformed HTML pages, we need to keep the +static behavior. + +I think of something like that: + + >>> server = PyPIMockServer() + >>> server.startHTTP() + >>> server.startXMLRPC() + +Then, the server must have only one port to rely on, eg. + + >>> server.fulladress() + "http://ip:port/" + +It could be simple to have one HTTP server, relaying the requests to the two +implementations (static HTTP and XMLRPC over HTTP). """ import Queue @@ -79,7 +103,8 @@ threading.Thread.__init__(self) self._run = True self._serve_xmlrpc = serve_xmlrpc - + + #TODO allow to serve XMLRPC and HTTP static files at the same time. if not self._serve_xmlrpc: self.server = HTTPServer(('', 0), PyPIRequestHandler) self.server.RequestHandlerClass.pypi_server = self @@ -97,7 +122,7 @@ self.static_filesystem_paths = [PYPI_DEFAULT_STATIC_PATH + "/" + path for path in static_filesystem_paths] else: - # xmlrpc server + # XMLRPC server self.server = PyPIXMLRPCServer(('', 0)) self.xmlrpc = XMLRPCMockIndex() # register the xmlrpc methods @@ -331,7 +356,8 @@ 'summary': self.summary, 'home_page': self.homepage, 'stable_version': self.stable_version, - 'provides_dist': self.provides_dist, + 'provides_dist': self.provides_dist or "%s (%s)" % (self.name, + self.version), 'requires': self.requires, 'cheesecake_installability_id': self.cheesecake_installability_id, } -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:47 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:47 +0200 Subject: [Python-checkins] distutils2: Some pep8 and pychecker improvements Message-ID: tarek.ziade pushed 292c0b541b47 to distutils2: http://hg.python.org/distutils2/rev/292c0b541b47 changeset: 494:292c0b541b47 user: ?ric Araujo date: Fri Aug 06 06:33:22 2010 +0200 summary: Some pep8 and pychecker improvements files: src/distutils2/dist.py, src/distutils2/metadata.py diff --git a/src/distutils2/dist.py b/src/distutils2/dist.py --- a/src/distutils2/dist.py +++ b/src/distutils2/dist.py @@ -41,7 +41,6 @@ See the code for 'setup()', in core.py, for details. """ - # 'global_options' describes the command-line options that may be # supplied to the setup script prior to any actual commands. # Eg. "./setup.py -n" or "./setup.py --quiet" both take advantage of @@ -274,10 +273,10 @@ and return the new dictionary; otherwise, return the existing option dictionary. """ - dict = self.command_options.get(command) - if dict is None: - dict = self.command_options[command] = {} - return dict + d = self.command_options.get(command) + if d is None: + d = self.command_options[command] = {} + return d def get_fullname(self): return self.metadata.get_fullname() @@ -373,7 +372,7 @@ for opt in options: if opt != '__name__': - val = parser.get(section,opt) + val = parser.get(section, opt) opt = opt.replace('-', '_') # ... although practicality beats purity :( @@ -402,12 +401,12 @@ try: if alias: setattr(self, alias, not strtobool(val)) - elif opt in ('verbose', 'dry_run'): # ugh! + elif opt in ('verbose', 'dry_run'): # ugh! setattr(self, opt, strtobool(val)) else: setattr(self, opt, val) except ValueError, msg: - raise DistutilsOptionError, msg + raise DistutilsOptionError(msg) # -- Command-line parsing methods ---------------------------------- @@ -473,7 +472,7 @@ # Oops, no commands found -- an end-user error if not self.commands: - raise DistutilsArgError, "no commands supplied" + raise DistutilsArgError("no commands supplied") # All is well: return true return 1 @@ -504,7 +503,7 @@ # Pull the current command from the head of the command line command = args[0] if not command_re.match(command): - raise SystemExit, "invalid command name '%s'" % command + raise SystemExit("invalid command name '%s'" % command) self.commands.append(command) # Dig up the command class that implements this command, so we @@ -513,22 +512,21 @@ try: cmd_class = self.get_command_class(command) except DistutilsModuleError, msg: - raise DistutilsArgError, msg + raise DistutilsArgError(msg) # Require that the command class be derived from Command -- want # to be sure that the basic "command" interface is implemented. if not issubclass(cmd_class, Command): - raise DistutilsClassError, \ - "command class %s must subclass Command" % cmd_class + raise DistutilsClassError( + "command class %s must subclass Command" % cmd_class) # Also make sure that the command object provides a list of its # known options. if not (hasattr(cmd_class, 'user_options') and isinstance(cmd_class.user_options, list)): - raise DistutilsClassError, \ - ("command class %s must provide " + - "'user_options' attribute (a list of tuples)") % \ - cmd_class + raise DistutilsClassError( + ("command class %s must provide " + "'user_options' attribute (a list of tuples)") % cmd_class) # If the command class has a list of negative alias options, # merge it in with the global negative aliases. @@ -545,7 +543,6 @@ else: help_options = [] - # All commands support the global options too, just by adding # in 'global_options'. parser.set_option_table(self.global_options + @@ -809,9 +806,9 @@ try: cls = getattr(module, class_name) except AttributeError: - raise DistutilsModuleError, \ - "invalid command '%s' (no class '%s' in module '%s')" \ - % (command, class_name, module_name) + raise DistutilsModuleError( + "invalid command '%s' (no class '%s' in module '%s')" % + (command, class_name, module_name)) self.cmdclass[command] = cls return cls @@ -880,11 +877,11 @@ elif hasattr(command_obj, option): setattr(command_obj, option, value) else: - raise DistutilsOptionError, \ - ("error in %s: command '%s' has no such option '%s'" - % (source, command_name, option)) + raise DistutilsOptionError( + "error in %s: command '%s' has no such option '%s'" % + (source, command_name, option)) except ValueError, msg: - raise DistutilsOptionError, msg + raise DistutilsOptionError(msg) def get_reinitialized_command(self, command, reinit_subcommands=0): """Reinitializes a command to the state it was in when first diff --git a/src/distutils2/metadata.py b/src/distutils2/metadata.py --- a/src/distutils2/metadata.py +++ b/src/distutils2/metadata.py @@ -3,10 +3,10 @@ Supports all metadata formats (1.0, 1.1, 1.2). """ -import re import os import sys import platform +import re from StringIO import StringIO from email import message_from_file from tokenize import tokenize, NAME, OP, STRING, ENDMARKER @@ -92,6 +92,7 @@ return _345_FIELDS raise MetadataUnrecognizedVersionError(version) + def _best_version(fields): """Detect the best version depending on the fields used.""" def _has_marker(keys, markers): @@ -448,7 +449,7 @@ missing.append(attr) if _HAS_DOCUTILS: - warnings = self._check_rst_data(self['Description']) + warnings.extend(self._check_rst_data(self['Description'])) # checking metadata 1.2 (XXX needs to check 1.1, 1.0) if self['Metadata-Version'] != '1.2': @@ -497,6 +498,7 @@ 'in': lambda x, y: x in y, 'not in': lambda x, y: x not in y} + def _operate(operation, x, y): return _OPERATORS[operation](x, y) @@ -508,6 +510,7 @@ 'platform.version': platform.version(), 'platform.machine': platform.machine()} + class _Operation(object): def __init__(self, execution_context=None): @@ -568,6 +571,7 @@ right = self._convert(self.right) return _operate(self.op, left, right) + class _OR(object): def __init__(self, left, right=None): self.left = left @@ -597,6 +601,7 @@ def __call__(self): return self.left() and self.right() + class _CHAIN(object): def __init__(self, execution_context=None): @@ -658,6 +663,7 @@ return False return True + def _interpret(marker, execution_context=None): """Interpret a marker and return a result depending on environment.""" marker = marker.strip() -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:47 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:47 +0200 Subject: [Python-checkins] distutils2: Remove property lazy loading for indexes. Message-ID: tarek.ziade pushed e270dcf660b2 to distutils2: http://hg.python.org/distutils2/rev/e270dcf660b2 changeset: 500:e270dcf660b2 user: Alexis Metaireau date: Fri Aug 06 17:10:25 2010 +0200 summary: Remove property lazy loading for indexes. files: docs/source/projects-index.dist.rst, docs/source/projects-index.xmlrpc.rst, src/distutils2/index/dist.py diff --git a/docs/source/projects-index.dist.rst b/docs/source/projects-index.dist.rst --- a/docs/source/projects-index.dist.rst +++ b/docs/source/projects-index.dist.rst @@ -84,26 +84,27 @@ {'url': 'http://example.org/foobar-1.0.tar.gz', 'hashname': None, 'hashval': None, 'is_external': True} -Attributes Lazy loading ------------------------ +Getting attributes from the dist objects +----------------------------------------- To abstract a maximum the way of querying informations to the indexes, -attributes and releases informations can be retrieved "on demand", in a "lazy" -way. +attributes and releases informations can be retrieved directly from the objects +returned by the indexes. For instance, if you have a release instance that does not contain the metadata -attribute, it can be build directly when accedded:: +attribute, it can be fetched by using the "fetch_metadata" method:: >>> r = Release("FooBar", "1.1") - >>> print r._metadata + >>> print r.metadata None # metadata field is actually set to "None" - >>> r.metadata + >>> r.fetch_metadata() -Like this, it's possible to retrieve project's releases, releases metadata and -releases distributions informations. +Like this, it's possible to retrieve project's releases (`fetch_releases`), +releases metadata (`fetch_metadata` and releases distributions +(`fetch_distributions` informations). Internally, this is possible because while retrieving for the first time informations about projects, releases or distributions, a reference to the -client used is stored in the objects. Then, while trying to access undefined -fields, it will be used if necessary. +client used is stored in the objects (can be accessed using the object +`_index` attribute. diff --git a/docs/source/projects-index.xmlrpc.rst b/docs/source/projects-index.xmlrpc.rst --- a/docs/source/projects-index.xmlrpc.rst +++ b/docs/source/projects-index.xmlrpc.rst @@ -126,24 +126,3 @@ As you see, this does not return a list of distributions, but a release, because a release can be used like a list of distributions. - -Lazy load information from project, releases and distributions. ----------------------------------------------------------------- - -.. note:: The lazy loading feature is not currently available ! - -As :mod:`distutils2.index.dist` classes support "lazy" loading of -informations, you can use it while retrieving informations from XML-RPC. - -For instance, it's possible to get all the releases for a project, and to access -directly the metadata of each release, without making -:class:`distutils2.index.xmlrpc.Client` directly (they will be made, but they're -invisible to the you):: - - >>> client = xmlrpc.Client() - >>> releases = client.get_releases("FooBar") - >>> releases.get_release("1.1").metadata - - -Refer to the :mod:`distutils2.index.dist` documentation for more information -about attributes lazy loading. diff --git a/src/distutils2/index/dist.py b/src/distutils2/index/dist.py --- a/src/distutils2/index/dist.py +++ b/src/distutils2/index/dist.py @@ -65,10 +65,10 @@ self._version = None self.version = version if metadata: - self._metadata = DistributionMetadata(mapping=metadata) + self.metadata = DistributionMetadata(mapping=metadata) else: - self._metadata = None - self._dists = {} + self.metadata = None + self.dists = {} self.hidden = hidden if 'dist_type' in kwargs: @@ -90,25 +90,23 @@ version = property(get_version, set_version) - @property - def metadata(self): + def fetch_metadata(self): """If the metadata is not set, use the indexes to get it""" - if not self._metadata: + if not self.metadata: self._index.get_metadata(self.name, '%s' % self.version) - return self._metadata + return self.metadata @property def is_final(self): """proxy to version.is_final""" return self.version.is_final - @property - def dists(self): - if self._dists is None: + def fetch_distributions(self): + if self.dists is None: self._index.get_distributions(self.name, '%s' % self.version) - if self._dists is None: - self._dists = {} - return self._dists + if self.dists is None: + self.dists = {} + return self.dists def add_distribution(self, dist_type='sdist', python_version=None, **params): """Add distribution informations to this release. @@ -123,12 +121,12 @@ if dist_type not in DIST_TYPES: raise ValueError(dist_type) if dist_type in self.dists: - self._dists[dist_type].add_url(**params) + self.dists[dist_type].add_url(**params) else: - self._dists[dist_type] = DistInfo(self, dist_type, + self.dists[dist_type] = DistInfo(self, dist_type, index=self._index, **params) if python_version: - self._dists[dist_type].python_version = python_version + self.dists[dist_type].python_version = python_version def get_distribution(self, dist_type=None, prefer_source=True): """Return a distribution. @@ -164,9 +162,9 @@ .download(path=temp_path) def set_metadata(self, metadata): - if not self._metadata: - self._metadata = DistributionMetadata() - self._metadata.update(metadata) + if not self.metadata: + self.metadata = DistributionMetadata() + self.metadata.update(metadata) def __getitem__(self, item): """distributions are available using release["sdist"]""" @@ -352,18 +350,12 @@ """ def __init__(self, name, releases=None, contains_hidden=False, index=None): self.set_index(index) - self._releases = [] + self.releases = [] self.name = name self.contains_hidden = contains_hidden if releases: self.add_releases(releases) - @property - def releases(self): - if not self._releases: - self.fetch_releases() - return self._releases - def fetch_releases(self): self._index.get_releases(self.name) return self.releases @@ -414,16 +406,16 @@ if not version in self.get_versions(): # append only if not already exists - self._releases.append(release) + self.releases.append(release) for dist in release.dists.values(): for url in dist.urls: self.add_release(version, dist.dist_type, **url) else: - matches = [r for r in self._releases if '%s' % r.version == version + matches = [r for r in self.releases if '%s' % r.version == version and r.name == self.name] if not matches: release = ReleaseInfo(self.name, version, index=self._index) - self._releases.append(release) + self.releases.append(release) else: release = matches[0] @@ -461,7 +453,7 @@ def get_versions(self): """Return a list of releases versions contained""" - return ["%s" % r.version for r in self._releases] + return ["%s" % r.version for r in self.releases] def __getitem__(self, key): return self.releases[key] -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:47 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:47 +0200 Subject: [Python-checkins] distutils2: Index crawling. Raise an exception when no release match the given requirements. Message-ID: tarek.ziade pushed 2f87dcc4b65f to distutils2: http://hg.python.org/distutils2/rev/2f87dcc4b65f changeset: 501:2f87dcc4b65f user: Alexis Metaireau date: Fri Aug 06 17:12:08 2010 +0200 summary: Index crawling. Raise an exception when no release match the given requirements. files: src/distutils2/index/xmlrpc.py diff --git a/src/distutils2/index/xmlrpc.py b/src/distutils2/index/xmlrpc.py --- a/src/distutils2/index/xmlrpc.py +++ b/src/distutils2/index/xmlrpc.py @@ -3,7 +3,8 @@ from distutils2.errors import IrrationalVersionError from distutils2.index.base import BaseClient -from distutils2.index.errors import ProjectNotFound, InvalidSearchField +from distutils2.index.errors import (ProjectNotFound, InvalidSearchField, + ReleaseNotFound) from distutils2.index.dist import ReleaseInfo from distutils2.version import get_version_predicate @@ -97,6 +98,8 @@ index=self._index) for version in versions]) project = project.filter(predicate) + if len(project) == 0: + raise ReleaseNotFound("%s" % predicate) project.sort_releases(prefer_final) return project -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:48 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:48 +0200 Subject: [Python-checkins] distutils2: Finish renaming of some options, clarify old record option help Message-ID: tarek.ziade pushed f0e7bd08ac2f to distutils2: http://hg.python.org/distutils2/rev/f0e7bd08ac2f changeset: 509:f0e7bd08ac2f user: ?ric Araujo date: Sat Aug 07 17:50:42 2010 +0200 summary: Finish renaming of some options, clarify old record option help files: src/distutils2/command/install.py, src/distutils2/tests/test_install.py diff --git a/src/distutils2/command/install.py b/src/distutils2/command/install.py --- a/src/distutils2/command/install.py +++ b/src/distutils2/command/install.py @@ -77,8 +77,10 @@ #('install-html=', None, "directory for HTML documentation"), #('install-info=', None, "directory for GNU info files"), + # XXX use a name that makes clear this is the old format ('record=', None, - "filename in which to record list of installed files"), + "filename in which to record a list of installed files " + "(not PEP 376-compliant)"), # .dist-info related arguments, read by install_dist_info ('no-distinfo', None, 'do not create a .dist-info directory'), @@ -88,8 +90,8 @@ ('no-record', None, 'do not generate a RECORD file'), ] - boolean_options = ['compile', 'force', 'skip-build', 'no-dist-info', - 'requested', 'no-dist-record',] + boolean_options = ['compile', 'force', 'skip-build', 'no-distinfo', + 'requested', 'no-record'] user_options.append(('user', None, "install in user site-package '%s'" % \ diff --git a/src/distutils2/tests/test_install.py b/src/distutils2/tests/test_install.py --- a/src/distutils2/tests/test_install.py +++ b/src/distutils2/tests/test_install.py @@ -202,6 +202,9 @@ finally: f.close() + # XXX test that fancy_getopt is okay with options named + # record and no-record but unrelated + def _test_debug_mode(self): # this covers the code called when DEBUG is set old_logs_len = len(self.logs) -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:48 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:48 +0200 Subject: [Python-checkins] distutils2: Remove redundant redundancy Message-ID: tarek.ziade pushed d95b3f0dfe62 to distutils2: http://hg.python.org/distutils2/rev/d95b3f0dfe62 changeset: 512:d95b3f0dfe62 user: ?ric Araujo date: Sat Aug 07 18:05:27 2010 +0200 summary: Remove redundant redundancy files: src/distutils2/command/install_distinfo.py diff --git a/src/distutils2/command/install_distinfo.py b/src/distutils2/command/install_distinfo.py --- a/src/distutils2/command/install_distinfo.py +++ b/src/distutils2/command/install_distinfo.py @@ -54,9 +54,7 @@ def finalize_options(self): self.set_undefined_options('install', - ('installer', 'installer'), - ('requested', 'requested'), - ('no_record', 'no_record')) + 'installer', 'requested', 'no_record') self.set_undefined_options('install_lib', ('install_dir', 'distinfo_dir')) -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:47 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:47 +0200 Subject: [Python-checkins] distutils2: Tweak docstring of tests.support Message-ID: tarek.ziade pushed ad7468195377 to distutils2: http://hg.python.org/distutils2/rev/ad7468195377 changeset: 506:ad7468195377 user: ?ric Araujo date: Sat Aug 07 13:29:51 2010 +0200 summary: Tweak docstring of tests.support files: src/distutils2/tests/support.py diff --git a/src/distutils2/tests/support.py b/src/distutils2/tests/support.py --- a/src/distutils2/tests/support.py +++ b/src/distutils2/tests/support.py @@ -1,7 +1,7 @@ """Support code for distutils2 test cases. Always import unittest from this module, it will be the right version -(standard library unittest for 2.7 and higher, third-party unittest2 +(standard library unittest for 3.2 and higher, third-party unittest2 release for older versions). Three helper classes are provided: LoggingSilencer, TempdirManager and @@ -17,10 +17,10 @@ tearDown): def setUp(self): - super(self.__class__, self).setUp() + super(SomeTestCase, self).setUp() ... # other setup code -Read each class' docstring to see their purpose and usage. +Read each class' docstring to see its purpose and usage. Also provided is a DummyCommand class, useful to mock commands in the tests of another command that needs them (see docstring). -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:47 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:47 +0200 Subject: [Python-checkins] distutils2: Add cheers in tests.sh Message-ID: tarek.ziade pushed 47e8fd8c67ba to distutils2: http://hg.python.org/distutils2/rev/47e8fd8c67ba changeset: 505:47e8fd8c67ba user: ?ric Araujo date: Fri Aug 06 18:07:53 2010 +0200 summary: Add cheers in tests.sh files: src/tests.sh diff --git a/src/tests.sh b/src/tests.sh --- a/src/tests.sh +++ b/src/tests.sh @@ -4,36 +4,37 @@ python2.4 setup.py build_ext -f -q 2> /dev/null > /dev/null python2.4 -Wd runtests.py -q 2> /dev/null if [ $? -ne 0 ];then - echo "Failed" + echo Failed rm -f distutils2/_backport/_hashlib.so exit 1 else - echo "Success" + echo Success fi echo -n "Running tests for Python 2.5... " python2.5 -Wd runtests.py -q 2> /dev/null if [ $? -ne 0 ];then - echo "Failed" + echo Failed exit 1 else - echo "Success" + echo Success fi echo -n "Running tests for Python 2.6... " python2.6 -Wd runtests.py -q 2> /dev/null if [ $? -ne 0 ];then - echo "Failed" + echo Failed exit 1 else - echo "Success" + echo Success fi echo -n "Running tests for Python 2.7... " python2.7 -Wd -bb -3 runtests.py -q 2> /dev/null if [ $? -ne 0 ];then - echo "Failed" + echo Failed exit 1 else - echo "Success" + echo Success fi +echo Good job, commit now! -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:48 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:48 +0200 Subject: [Python-checkins] distutils2: Add a comment about one ambiguity in PEP 376, remove obsolete comment Message-ID: tarek.ziade pushed fa01e4f9584e to distutils2: http://hg.python.org/distutils2/rev/fa01e4f9584e changeset: 511:fa01e4f9584e user: ?ric Araujo date: Sat Aug 07 18:01:14 2010 +0200 summary: Add a comment about one ambiguity in PEP 376, remove obsolete comment files: src/distutils2/command/install_distinfo.py diff --git a/src/distutils2/command/install_distinfo.py b/src/distutils2/command/install_distinfo.py --- a/src/distutils2/command/install_distinfo.py +++ b/src/distutils2/command/install_distinfo.py @@ -62,6 +62,8 @@ ('install_dir', 'distinfo_dir')) if self.installer is None: + # FIXME distutils or distutils2? + # + document default in the option help text above and in install self.installer = 'distutils' if self.requested is None: self.requested = True @@ -144,10 +146,7 @@ return self.outputs -# The following routines are taken from setuptools' pkg_resources module and -# can be replaced by importing them from pkg_resources once it is included -# in the stdlib. - +# The following functions are taken from setuptools' pkg_resources module. def safe_name(name): """Convert an arbitrary string to a standard distribution name -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:47 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:47 +0200 Subject: [Python-checkins] distutils2: Added firsts bit of work about the installation script. Message-ID: tarek.ziade pushed 3d5db5aa326a to distutils2: http://hg.python.org/distutils2/rev/3d5db5aa326a changeset: 503:3d5db5aa326a user: Alexis Metaireau date: Fri Aug 06 17:14:32 2010 +0200 summary: Added firsts bit of work about the installation script. files: src/distutils2/install_with_deps.py, src/distutils2/tests/test_install_with_deps.py diff --git a/src/distutils2/install_with_deps.py b/src/distutils2/install_with_deps.py new file mode 100644 --- /dev/null +++ b/src/distutils2/install_with_deps.py @@ -0,0 +1,171 @@ +import logging +from distutils2.index import wrapper +from distutils2.index.errors import ProjectNotFound, ReleaseNotFound +from distutils2.depgraph import generate_graph +from distutils2._backport.pkgutil import get_distributions + + +"""Provides installations scripts. + +The goal of this script is to install a release from the indexes (eg. +PyPI), including the dependencies of the releases if needed. + +It uses the work made in pkgutil and by the index crawlers to browse the +installed distributions, and rely on the instalation commands to install. +""" + + +def get_deps(requirements, index): + """Return the dependencies of a project, as a depgraph object. + + Build a :class depgraph.DependencyGraph: for the given requirements + + If the project does not uses Metadata < 1.1, dependencies can't be handled + from here, so it returns an empty list of dependencies. + + :param requirements: is a string containing the version predicate to take + the project name and version specifier from. + :param index: the index to use for making searches. + """ + deps = [] + release = index.get_release(requirements) + requires = release.metadata['Requires-Dist'] + release.metadata['Requires'] + deps.append(release) # include the release we are computing deps. + for req in requires: + deps.extend(get_deps(req, index)) + return deps + + +def install(requirements, index=None, interactive=True, upgrade=True, + prefer_source=True, prefer_final=True): + """Given a list of distributions to install, a list of distributions to + remove, and a list of conflicts, proceed and do what's needed to be done. + + :param requirements: is a *string* containing the requirements for this + project (for instance "FooBar 1.1" or "BarBaz (<1.2) + :param index: If an index is specified, use this one, otherwise, use + :class index.ClientWrapper: to get project metadatas. + :param interactive: if set to True, will prompt the user for interactions + of needed. If false, use the default values. + :param upgrade: If a project exists in a newer version, does the script + need to install the new one, or keep the already installed + version. + :param prefer_source: used to tell if the user prefer source distributions + over built dists. + :param prefer_final: if set to true, pick up the "final" versions (eg. + stable) over the beta, alpha (not final) ones. + """ + # get the default index if none is specified + if not index: + index = wrapper.WrapperClient() + + # check if the project is already installed. + installed_release = get_installed_release(requirements) + + # if a version that satisfy the requirements is already installed + if installed_release and (interactive or upgrade): + new_releases = index.get_releases(requirements) + if (new_releases.get_last(requirements).version > + installed_release.version): + if interactive: + # prompt the user to install the last version of the package. + # set upgrade here. + print "You want to install a package already installed on your" + "system. A new version exists, you could just use the version" + "you have, or upgrade to the latest version" + + upgrade = raw_input("Do you want to install the most recent one ? (Y/n)") or "Y" + if upgrade in ('Y', 'y'): + upgrade = True + else: + upgrade = False + if not upgrade: + return + + # create the depgraph from the dependencies of the release we want to + # install + graph = generate_graph(get_deps(requirements, index)) + from ipdb import set_trace + set_trace() + installed = [] # to uninstall on errors + try: + for release in graph.adjacency_list: + dist = release.get_distribution() + install(dist) + installed.append(dist) + print "%s have been installed on your system" % requirements + except: + print "an error has occured, uninstalling" + for dist in installed: + uninstall_dist(dist) + +class InstallationException(Exception): + pass + +def get_install_info(requirements, index=None, already_installed=None): + """Return the informations on what's going to be installed and upgraded. + + :param requirements: is a *string* containing the requirements for this + project (for instance "FooBar 1.1" or "BarBaz (<1.2)") + :param index: If an index is specified, use this one, otherwise, use + :class index.ClientWrapper: to get project metadatas. + :param already_installed: a list of already installed distributions. + + The results are returned in a dict. For instance:: + + >>> get_install_info("FooBar (<=1.2)") + {'install': [], 'remove': [], 'conflict': []} + + Conflict contains all the conflicting distributions, if there is a + conflict. + + """ + def update_infos(new_infos, infos): + for key, value in infos.items(): + if key in new_infos: + infos[key].extend(new_infos[key]) + return new_infos + + if not index: + index = wrapper.ClientWrapper() + logging.info("searching releases for %s" % requirements) + + # 1. get all the releases that match the requirements + try: + releases = index.get_releases(requirements) + except (ReleaseNotFound, ProjectNotFound), e: + raise InstallationException('Release not found: "%s"' % requirements) + + # 2. pick up a release, and try to get the dependency tree + release = releases.get_last(requirements) + metadata = release.fetch_metadata() + + # 3. get the distributions already_installed on the system + # 4. and add the one we want to install + if not already_installed: + already_installed = get_distributions() + + logging.info("fetching %s %s dependencies" % ( + release.name, release.version)) + distributions = already_installed + [release] + depgraph = generate_graph(distributions) + + # store all the already_installed packages in a list, in case of rollback. + infos = {'install':[], 'remove': [], 'conflict': []} + + # 5. get what the missing deps are + for dists in depgraph.missing.values(): + if dists: + logging.info("missing dependencies found, installing them") + # we have missing deps + for dist in dists: + update_infos(get_install_info(dist, index, already_installed), + infos) + + # 6. fill in the infos + existing = [d for d in already_installed if d.name == release.name] + if existing: + infos['remove'].append(existing[0]) + infos['conflict'].extend(depgraph.reverse_list[existing[0]]) + infos['install'].append(release) + return infos diff --git a/src/distutils2/tests/test_install_with_deps.py b/src/distutils2/tests/test_install_with_deps.py new file mode 100644 --- /dev/null +++ b/src/distutils2/tests/test_install_with_deps.py @@ -0,0 +1,152 @@ +"""Tests for the distutils2.index.xmlrpc module.""" + +from distutils2.tests.pypi_server import use_xmlrpc_server +from distutils2.tests import run_unittest +from distutils2.tests.support import unittest +from distutils2.index.xmlrpc import Client +from distutils2.install_with_deps import (get_install_info, + InstallationException) +from distutils2.metadata import DistributionMetadata + +class FakeDist(object): + """A fake distribution object, for tests""" + def __init__(self, name, version, deps): + self.name = name + self.version = version + self.metadata = DistributionMetadata() + self.metadata['Requires-Dist'] = deps + self.metadata['Provides-Dist'] = ['%s (%s)' % (name, version)] + + def __repr__(self): + return '' % self.name + +def get_fake_dists(dists): + objects = [] + for (name, version, deps) in dists: + objects.append(FakeDist(name, version, deps)) + return objects + +class TestInstallWithDeps(unittest.TestCase): + def _get_client(self, server, *args, **kwargs): + return Client(server.full_address, *args, **kwargs) + + @use_xmlrpc_server() + def test_existing_deps(self, server): + # Test that the installer get the dependencies from the metadatas + # and ask the index for this dependencies. + # In this test case, we have choxie that is dependent from towel-stuff + # 0.1, which is in-turn dependent on bacon <= 0.2: + # choxie -> towel-stuff -> bacon. + # Each release metadata is not provided in metadata 1.2. + client = self._get_client(server) + archive_path = '%s/distribution.tar.gz' % server.full_address + server.xmlrpc.set_distributions([ + {'name':'choxie', + 'version': '2.0.0.9', + 'requires_dist': ['towel-stuff (0.1)',], + 'url': archive_path}, + {'name':'towel-stuff', + 'version': '0.1', + 'requires_dist': ['bacon (<= 0.2)',], + 'url': archive_path}, + {'name':'bacon', + 'version': '0.1', + 'requires_dist': [], + 'url': archive_path}, + ]) + installed = get_fake_dists([('bacon', '0.1', []),]) + output = get_install_info("choxie", index=client, + already_installed=installed) + + # we dont have installed bacon as it's already installed on the system. + self.assertEqual(0, len(output['remove'])) + self.assertEqual(2, len(output['install'])) + readable_output = [(o.name, '%s' % o.version) + for o in output['install']] + self.assertIn(('towel-stuff', '0.1'), readable_output) + self.assertIn(('choxie', '2.0.0.9'), readable_output) + + @use_xmlrpc_server() + def test_upgrade_existing_deps(self, server): + # Tests that the existing distributions can be upgraded if needed. + client = self._get_client(server) + archive_path = '%s/distribution.tar.gz' % server.full_address + server.xmlrpc.set_distributions([ + {'name':'choxie', + 'version': '2.0.0.9', + 'requires_dist': ['towel-stuff (0.1)',], + 'url': archive_path}, + {'name':'towel-stuff', + 'version': '0.1', + 'requires_dist': ['bacon (>= 0.2)',], + 'url': archive_path}, + {'name':'bacon', + 'version': '0.2', + 'requires_dist': [], + 'url': archive_path}, + ]) + + output = get_install_info("choxie", index=client, already_installed= + get_fake_dists([('bacon', '0.1', []),])) + installed = [(o.name, '%s' % o.version) for o in output['install']] + + # we need bacon 0.2, but 0.1 is installed. + # So we expect to remove 0.1 and to install 0.2 instead. + remove = [(o.name, '%s' % o.version) for o in output['remove']] + self.assertIn(('choxie', '2.0.0.9'), installed) + self.assertIn(('towel-stuff', '0.1'), installed) + self.assertIn(('bacon', '0.2'), installed) + self.assertIn(('bacon', '0.1'), remove) + self.assertEqual(0, len(output['conflict'])) + + @use_xmlrpc_server() + def test_conflicts(self, server): + # Tests that conflicts are detected + client = self._get_client(server) + archive_path = '%s/distribution.tar.gz' % server.full_address + server.xmlrpc.set_distributions([ + {'name':'choxie', + 'version': '2.0.0.9', + 'requires_dist': ['towel-stuff (0.1)',], + 'url': archive_path}, + {'name':'towel-stuff', + 'version': '0.1', + 'requires_dist': ['bacon (>= 0.2)',], + 'url': archive_path}, + {'name':'bacon', + 'version': '0.2', + 'requires_dist': [], + 'url': archive_path}, + ]) + already_installed = [('bacon', '0.1', []), + ('chicken', '1.1', ['bacon (0.1)'])] + output = get_install_info("choxie", index=client, already_installed= + get_fake_dists(already_installed)) + + # we need bacon 0.2, but 0.1 is installed. + # So we expect to remove 0.1 and to install 0.2 instead. + installed = [(o.name, '%s' % o.version) for o in output['install']] + remove = [(o.name, '%s' % o.version) for o in output['remove']] + conflict = [(o.name, '%s' % o.version) for o in output['conflict']] + self.assertIn(('choxie', '2.0.0.9'), installed) + self.assertIn(('towel-stuff', '0.1'), installed) + self.assertIn(('bacon', '0.2'), installed) + self.assertIn(('bacon', '0.1'), remove) + self.assertIn(('chicken', '1.1'), conflict) + + @use_xmlrpc_server() + def test_installation_unexisting_project(self, server): + # Test that the isntalled raises an exception if the project does not + # exists. + client = self._get_client(server) + self.assertRaises(InstallationException, get_install_info, + 'unexistant project', index=client) + + +def test_suite(): + suite = unittest.TestSuite() + suite.addTest(unittest.makeSuite(TestInstallWithDeps)) + return suite + +if __name__ == '__main__': + run_unittest(test_suite()) -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:47 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:47 +0200 Subject: [Python-checkins] distutils2: Tweak command.__all__. Message-ID: tarek.ziade pushed 55175bbe2a45 to distutils2: http://hg.python.org/distutils2/rev/55175bbe2a45 changeset: 508:55175bbe2a45 user: ?ric Araujo date: Sat Aug 07 15:18:13 2010 +0200 summary: Tweak command.__all__. files: src/distutils2/command/__init__.py diff --git a/src/distutils2/command/__init__.py b/src/distutils2/command/__init__.py --- a/src/distutils2/command/__init__.py +++ b/src/distutils2/command/__init__.py @@ -5,7 +5,8 @@ __revision__ = "$Id: __init__.py 71473 2009-04-11 14:55:07Z tarek.ziade $" -__all__ = ['build', +__all__ = ['check', + 'build', 'build_py', 'build_ext', 'build_clib', @@ -18,16 +19,9 @@ 'install_data', 'install_distinfo', 'sdist', - 'register', 'bdist', 'bdist_dumb', 'bdist_wininst', + 'register', 'upload', - 'check', - # These two are reserved for future use: - #'bdist_sdux', - #'bdist_pkgtool', - # Note: - # bdist_packager is not included because it only provides - # an abstract base class ] -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:48 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:48 +0200 Subject: [Python-checkins] distutils2: Move one method to ease diffing with configure.py. Message-ID: tarek.ziade pushed d1a4938bc04b to distutils2: http://hg.python.org/distutils2/rev/d1a4938bc04b changeset: 514:d1a4938bc04b user: ?ric Araujo date: Sat Aug 07 18:16:04 2010 +0200 summary: Move one method to ease diffing with configure.py. files: src/distutils2/command/install.py diff --git a/src/distutils2/command/install.py b/src/distutils2/command/install.py --- a/src/distutils2/command/install.py +++ b/src/distutils2/command/install.py @@ -333,23 +333,6 @@ if self.no_distinfo is None: self.no_distinfo = False - def dump_dirs(self, msg): - """Dumps the list of user options.""" - from distutils2.fancy_getopt import longopt_xlate - log.debug(msg + ":") - for opt in self.user_options: - opt_name = opt[0] - if opt_name[-1] == "=": - opt_name = opt_name[0:-1] - if opt_name in self.negative_opt: - opt_name = self.negative_opt[opt_name] - opt_name = opt_name.translate(longopt_xlate) - val = not getattr(self, opt_name) - else: - opt_name = opt_name.translate(longopt_xlate) - val = getattr(self, opt_name) - log.debug(" %s: %s" % (opt_name, val)) - def finalize_unix(self): """Finalizes options for posix platforms.""" if self.install_base is not None or self.install_platbase is not None: @@ -412,6 +395,22 @@ raise DistutilsPlatformError, \ "I don't know how to install stuff on '%s'" % os.name + def dump_dirs(self, msg): + """Dumps the list of user options.""" + log.debug(msg + ":") + for opt in self.user_options: + opt_name = opt[0] + if opt_name[-1] == "=": + opt_name = opt_name[0:-1] + if opt_name in self.negative_opt: + opt_name = self.negative_opt[opt_name] + opt_name = opt_name.replace('-', '_') + val = not getattr(self, opt_name) + else: + opt_name = opt_name.replace('-', '_') + val = getattr(self, opt_name) + log.debug(" %s: %s" % (opt_name, val)) + def select_scheme(self, name): """Sets the install directories by applying the install schemes.""" # it's the caller's problem if they supply a bad name! -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:48 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:48 +0200 Subject: [Python-checkins] distutils2: User site directory doesn?t exist before 2.6 Message-ID: tarek.ziade pushed 950793edbee0 to distutils2: http://hg.python.org/distutils2/rev/950793edbee0 changeset: 513:950793edbee0 user: ?ric Araujo date: Sat Aug 07 18:08:29 2010 +0200 summary: User site directory doesn?t exist before 2.6 files: src/distutils2/command/install.py diff --git a/src/distutils2/command/install.py b/src/distutils2/command/install.py --- a/src/distutils2/command/install.py +++ b/src/distutils2/command/install.py @@ -98,11 +98,14 @@ boolean_options = ['compile', 'force', 'skip-build', 'no-distinfo', 'requested', 'no-record'] - user_options.append(('user', None, - "install in user site-package '%s'" % \ - get_path('purelib', '%s_user' % os.name))) - boolean_options.append('user') negative_opt = {'no-compile' : 'compile', 'no-requested': 'requested'} + if sys.version >= '2.6': + user_options.append( + ('user', None, + "install in user site-package [%s]" % + get_path('purelib', '%s_user' % os.name))) + + boolean_options.append('user') def initialize_options(self): @@ -112,6 +115,8 @@ self.prefix = None self.exec_prefix = None self.home = None + # This attribute is used all over the place, so it's best to + # define it even in < 2.6 self.user = 0 # These select only the installation base; it's up to the user to -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:48 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:48 +0200 Subject: [Python-checkins] distutils2: Make option lists respect style used in other commands Message-ID: tarek.ziade pushed 10a8c03c822f to distutils2: http://hg.python.org/distutils2/rev/10a8c03c822f changeset: 510:10a8c03c822f user: ?ric Araujo date: Sat Aug 07 17:52:41 2010 +0200 summary: Make option lists respect style used in other commands files: src/distutils2/command/install.py, src/distutils2/command/install_distinfo.py diff --git a/src/distutils2/command/install.py b/src/distutils2/command/install.py --- a/src/distutils2/command/install.py +++ b/src/distutils2/command/install.py @@ -83,11 +83,16 @@ "(not PEP 376-compliant)"), # .dist-info related arguments, read by install_dist_info - ('no-distinfo', None, 'do not create a .dist-info directory'), - ('installer=', None, 'the name of the installer'), - ('requested', None, 'generate a REQUESTED file'), - ('no-requested', None, 'do not generate a REQUESTED file'), - ('no-record', None, 'do not generate a RECORD file'), + ('no-distinfo', None, + "do not create a .dist-info directory"), + ('installer=', None, + "the name of the installer"), + ('requested', None, + "generate a REQUESTED file (i.e."), + ('no-requested', None, + "do not generate a REQUESTED file"), + ('no-record', None, + "do not generate a RECORD file"), ] boolean_options = ['compile', 'force', 'skip-build', 'no-distinfo', diff --git a/src/distutils2/command/install_distinfo.py b/src/distutils2/command/install_distinfo.py --- a/src/distutils2/command/install_distinfo.py +++ b/src/distutils2/command/install_distinfo.py @@ -31,18 +31,18 @@ user_options = [ ('distinfo-dir=', None, - 'directory where the the .dist-info directory will ' - 'be installed'), - ('installer=', None, 'the name of the installer'), - ('requested', None, 'generate a REQUESTED file'), - ('no-requested', None, 'do not generate a REQUESTED file'), - ('no-record', None, 'do not generate a RECORD file'), + "directory where the the .dist-info directory will be installed"), + ('installer=', None, + "the name of the installer"), + ('requested', None, + "generate a REQUESTED file"), + ('no-requested', None, + "do not generate a REQUESTED file"), + ('no-record', None, + "do not generate a RECORD file"), ] - boolean_options = [ - 'requested', - 'no-record', - ] + boolean_options = ['requested', 'no-record'] negative_opt = {'no-requested': 'requested'} -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:47 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:47 +0200 Subject: [Python-checkins] distutils2: Add missing install_distinfo in command.__all__ Message-ID: tarek.ziade pushed 9358e65c3bad to distutils2: http://hg.python.org/distutils2/rev/9358e65c3bad changeset: 507:9358e65c3bad user: ?ric Araujo date: Sat Aug 07 15:15:39 2010 +0200 summary: Add missing install_distinfo in command.__all__ files: src/distutils2/command/__init__.py diff --git a/src/distutils2/command/__init__.py b/src/distutils2/command/__init__.py --- a/src/distutils2/command/__init__.py +++ b/src/distutils2/command/__init__.py @@ -16,6 +16,7 @@ 'install_headers', 'install_scripts', 'install_data', + 'install_distinfo', 'sdist', 'register', 'bdist', -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:48 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:48 +0200 Subject: [Python-checkins] distutils2: Cleanup sweep (PEP 8) Message-ID: tarek.ziade pushed 7c9f24dc9fbc to distutils2: http://hg.python.org/distutils2/rev/7c9f24dc9fbc changeset: 515:7c9f24dc9fbc user: ?ric Araujo date: Sat Aug 07 18:19:03 2010 +0200 summary: Cleanup sweep (PEP 8) files: src/distutils2/command/install.py diff --git a/src/distutils2/command/install.py b/src/distutils2/command/install.py --- a/src/distutils2/command/install.py +++ b/src/distutils2/command/install.py @@ -18,6 +18,7 @@ from distutils2.util import convert_path, change_root, get_platform from distutils2.errors import DistutilsOptionError + class install(Command): description = "install everything from build directory" @@ -31,7 +32,7 @@ ('home=', None, "(Unix only) home directory to install under"), - # Or, just set the base director(y|ies) + # Or just set the base director(y|ies) ('install-base=', None, "base installation directory (instead of --prefix or --home)"), ('install-platbase=', None, @@ -40,7 +41,7 @@ ('root=', None, "install everything relative to this alternate root directory"), - # Or, explicitly set the installation scheme + # Or explicitly set the installation scheme ('install-purelib=', None, "installation directory for pure Python module distributions"), ('install-platlib=', None, @@ -62,8 +63,8 @@ ('compile', 'c', "compile .py to .pyc [default]"), ('no-compile', None, "don't compile .py files"), ('optimize=', 'O', - "also compile with optimization: -O1 for \"python -O\", " - "-O2 for \"python -OO\", and -O0 to disable [default: -O0]"), + 'also compile with optimization: -O1 for "python -O", ' + '-O2 for "python -OO", and -O0 to disable [default: -O0]'), # Miscellaneous control options ('force', 'f', @@ -98,7 +99,6 @@ boolean_options = ['compile', 'force', 'skip-build', 'no-distinfo', 'requested', 'no-record'] - negative_opt = {'no-compile' : 'compile', 'no-requested': 'requested'} if sys.version >= '2.6': user_options.append( ('user', None, @@ -107,9 +107,9 @@ boolean_options.append('user') + negative_opt = {'no-compile': 'compile', 'no-requested': 'requested'} def initialize_options(self): - """Initializes options.""" # High-level options: these select both an installation base # and scheme. self.prefix = None @@ -186,7 +186,6 @@ self.requested = None self.no_record = None - # -- Option finalizing methods ------------------------------------- # (This is rather more involved than for most commands, # because this is where the policy for installing third- @@ -194,7 +193,6 @@ # array of user input is decided. Yes, it's quite complex!) def finalize_options(self): - """Finalizes options.""" # This method (and its pliant slaves, like 'finalize_unix()', # 'finalize_other()', and 'select_scheme()') is where the default # installation directories for modules, extension modules, and @@ -211,18 +209,19 @@ if ((self.prefix or self.exec_prefix or self.home) and (self.install_base or self.install_platbase)): - raise DistutilsOptionError, \ - ("must supply either prefix/exec-prefix/home or " + - "install-base/install-platbase -- not both") + raise DistutilsOptionError( + "must supply either prefix/exec-prefix/home or " + "install-base/install-platbase -- not both") if self.home and (self.prefix or self.exec_prefix): - raise DistutilsOptionError, \ - "must supply either home or prefix/exec-prefix -- not both" + raise DistutilsOptionError( + "must supply either home or prefix/exec-prefix -- not both") if self.user and (self.prefix or self.exec_prefix or self.home or - self.install_base or self.install_platbase): - raise DistutilsOptionError("can't combine user with with prefix/" - "exec_prefix/home or install_(plat)base") + self.install_base or self.install_platbase): + raise DistutilsOptionError( + "can't combine user with prefix/exec_prefix/home or " + "install_base/install_platbase") # Next, stuff that's wrong (or dubious) only on certain platforms. if os.name != "posix": @@ -257,18 +256,19 @@ 'srcdir') metadata = self.distribution.metadata - self.config_vars = {'dist_name': metadata['Name'], - 'dist_version': metadata['Version'], - 'dist_fullname': metadata.get_fullname(), - 'py_version': py_version, - 'py_version_short': py_version[0:3], - 'py_version_nodot': py_version[0] + py_version[2], - 'sys_prefix': prefix, - 'prefix': prefix, - 'sys_exec_prefix': exec_prefix, - 'exec_prefix': exec_prefix, - 'srcdir': srcdir, - } + self.config_vars = { + 'dist_name': metadata['Name'], + 'dist_version': metadata['Version'], + 'dist_fullname': metadata.get_fullname(), + 'py_version': py_version, + 'py_version_short': py_version[:3], + 'py_version_nodot': py_version[:3:2], + 'sys_prefix': prefix, + 'prefix': prefix, + 'sys_exec_prefix': exec_prefix, + 'exec_prefix': exec_prefix, + 'srcdir': srcdir, + } self.config_vars['userbase'] = self.install_userbase self.config_vars['usersite'] = self.install_usersite @@ -296,12 +296,11 @@ # module distribution is pure or not. Of course, if the user # already specified install_lib, use their selection. if self.install_lib is None: - if self.distribution.ext_modules: # has extensions: non-pure + if self.distribution.ext_modules: # has extensions: non-pure self.install_lib = self.install_platlib else: self.install_lib = self.install_purelib - # Convert directories from Unix /-separated syntax to the local # convention. self.convert_paths('lib', 'purelib', 'platlib', @@ -313,7 +312,7 @@ # non-packagized module distributions (hello, Numerical Python!) to # get their own directories. self.handle_extra_path() - self.install_libbase = self.install_lib # needed for .pth file + self.install_libbase = self.install_lib # needed for .pth file self.install_lib = os.path.join(self.install_lib, self.extra_dirs) # If a new root directory was supplied, make all the installation @@ -334,7 +333,7 @@ self.no_distinfo = False def finalize_unix(self): - """Finalizes options for posix platforms.""" + """Finalize options for posix platforms.""" if self.install_base is not None or self.install_platbase is not None: if ((self.install_lib is None and self.install_purelib is None and @@ -342,15 +341,15 @@ self.install_headers is None or self.install_scripts is None or self.install_data is None): - raise DistutilsOptionError, \ - ("install-base or install-platbase supplied, but " - "installation scheme is incomplete") + raise DistutilsOptionError( + "install-base or install-platbase supplied, but " + "installation scheme is incomplete") return if self.user: if self.install_userbase is None: raise DistutilsPlatformError( - "User base directory is not specified") + "user base directory is not specified") self.install_base = self.install_platbase = self.install_userbase self.select_scheme("posix_user") elif self.home is not None: @@ -359,8 +358,8 @@ else: if self.prefix is None: if self.exec_prefix is not None: - raise DistutilsOptionError, \ - "must not supply exec-prefix without prefix" + raise DistutilsOptionError( + "must not supply exec-prefix without prefix") self.prefix = os.path.normpath(sys.prefix) self.exec_prefix = os.path.normpath(sys.exec_prefix) @@ -374,11 +373,11 @@ self.select_scheme("posix_prefix") def finalize_other(self): - """Finalizes options for non-posix platforms""" + """Finalize options for non-posix platforms""" if self.user: if self.install_userbase is None: raise DistutilsPlatformError( - "User base directory is not specified") + "user base directory is not specified") self.install_base = self.install_platbase = self.install_userbase self.select_scheme(os.name + "_user") elif self.home is not None: @@ -392,11 +391,11 @@ try: self.select_scheme(os.name) except KeyError: - raise DistutilsPlatformError, \ - "I don't know how to install stuff on '%s'" % os.name + raise DistutilsPlatformError( + "no support for installation on '%s'" % os.name) def dump_dirs(self, msg): - """Dumps the list of user options.""" + """Dump the list of user options.""" log.debug(msg + ":") for opt in self.user_options: opt_name = opt[0] @@ -412,7 +411,7 @@ log.debug(" %s: %s" % (opt_name, val)) def select_scheme(self, name): - """Sets the install directories by applying the install schemes.""" + """Set the install directories by applying the install schemes.""" # it's the caller's problem if they supply a bad name! scheme = get_paths(name, expand=False) for key, value in scheme.items(): @@ -435,15 +434,14 @@ setattr(self, attr, val) def expand_basedirs(self): - """Calls `os.path.expanduser` on install_base, install_platbase and - root.""" + """Call `os.path.expanduser` on install_{base,platbase} and root.""" self._expand_attrs(['install_base', 'install_platbase', 'root']) def expand_dirs(self): - """Calls `os.path.expanduser` on install dirs.""" + """Call `os.path.expanduser` on install dirs.""" self._expand_attrs(['install_purelib', 'install_platlib', 'install_lib', 'install_headers', - 'install_scripts', 'install_data',]) + 'install_scripts', 'install_data']) def convert_paths(self, *names): """Call `convert_path` over `names`.""" @@ -465,9 +463,9 @@ elif len(self.extra_path) == 2: path_file, extra_dirs = self.extra_path else: - raise DistutilsOptionError, \ - ("'extra_path' option must be a list, tuple, or " - "comma-separated string with 1 or 2 elements") + raise DistutilsOptionError( + "'extra_path' option must be a list, tuple, or " + "comma-separated string with 1 or 2 elements") # convert to local form in case Unix notation used (as it # should be in setup scripts) @@ -553,7 +551,6 @@ else: self.warn("path file '%s' not created" % filename) - # -- Reporting methods --------------------------------------------- def get_outputs(self): @@ -608,10 +605,10 @@ # 'sub_commands': a list of commands this command might have to run to # get its work done. See cmd.py for more info. - sub_commands = [('install_lib', has_lib), + sub_commands = [('install_lib', has_lib), ('install_headers', has_headers), ('install_scripts', has_scripts), - ('install_data', has_data), + ('install_data', has_data), # keep install_distinfo last, as it needs the record # with files to be completely generated ('install_distinfo', lambda self: not self.no_distinfo), -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:48 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:48 +0200 Subject: [Python-checkins] distutils2: Merged with Tarek's repository Message-ID: tarek.ziade pushed 85a805a00f82 to distutils2: http://hg.python.org/distutils2/rev/85a805a00f82 changeset: 518:85a805a00f82 parent: 517:6c333edc198e parent: 375:fdc0f44dc5ec user: Jeremy Kloth date: Sat Jul 17 19:33:50 2010 -0600 summary: Merged with Tarek's repository files: diff --git a/src/distutils2/converter/fixers/fix_imports.py b/src/distutils2/converter/fixers/fix_imports.py --- a/src/distutils2/converter/fixers/fix_imports.py +++ b/src/distutils2/converter/fixers/fix_imports.py @@ -36,11 +36,16 @@ pattern = [] next = imp.next_sibling while next is not None: + # Get the first child if we have a Node + if not hasattr(next, "value"): + next = next.children[0] pattern.append(next.value) if not hasattr(next, "next_sibling"): next.next_sibling = next.get_next_sibling() next = next.next_sibling - if pattern == ['import', 'setup']: + + if set(pattern).issubset(set( + ['import', ',', 'setup', 'find_packages'])): imp.value = 'distutils2.core' imp.changed() diff --git a/src/distutils2/tests/conversions/05_after.py b/src/distutils2/tests/conversions/05_after.py new file mode 100644 --- /dev/null +++ b/src/distutils2/tests/conversions/05_after.py @@ -0,0 +1,137 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright (C) 2003-2009 Edgewall Software +# All rights reserved. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at http://trac.edgewall.org/wiki/TracLicense. +# +# This software consists of voluntary contributions made by many +# individuals. For the exact contribution history, see the revision +# history and logs, available at http://trac.edgewall.org/log/. + +from distutils2.core import setup, find_packages + +extra = {} + +try: + import babel + + extractors = [ + ('**.py', 'python', None), + ('**/templates/**.html', 'genshi', None), + ('**/templates/**.txt', 'genshi', + {'template_class': 'genshi.template:NewTextTemplate'}), + ] + extra['message_extractors'] = { + 'trac': extractors, + 'tracopt': extractors, + } + + from trac.util.dist import get_l10n_js_cmdclass + extra['cmdclass'] = get_l10n_js_cmdclass() + +except ImportError, e: + pass + +setup( + name = 'Trac', + version = '0.12.1', + summary = 'Integrated SCM, wiki, issue tracker and project environment', + description = """ +Trac is a minimalistic web-based software project management and bug/issue +tracking system. It provides an interface to the Subversion revision control +systems, an integrated wiki, flexible issue tracking and convenient report +facilities. +""", + author = 'Edgewall Software', + author_email = 'info at edgewall.com', + license = 'BSD', + home_page = 'http://trac.edgewall.org/', + download_url = 'http://trac.edgewall.org/wiki/TracDownload', + classifiers = [ + 'Environment :: Web Environment', + 'Framework :: Trac', + 'Intended Audience :: Developers', + 'License :: OSI Approved :: BSD License', + 'Operating System :: OS Independent', + 'Programming Language :: Python', + 'Topic :: Software Development :: Bug Tracking', + 'Topic :: Software Development :: Version Control', + ], + + packages = find_packages(exclude=['*.tests']), + package_data = { + '': ['templates/*'], + 'trac': ['htdocs/*.*', 'htdocs/README', 'htdocs/js/*.*', + 'htdocs/js/messages/*.*', 'htdocs/css/*.*', + 'htdocs/guide/*', 'locale/*/LC_MESSAGES/messages.mo'], + 'trac.wiki': ['default-pages/*'], + 'trac.ticket': ['workflows/*.ini'], + }, + + test_suite = 'trac.test.suite', + zip_safe = True, + + requires_dist = [ + 'setuptools>=0.6b1', + 'Genshi>=0.6', + ], + extras_require = { + 'Babel': ['Babel>=0.9.5'], + 'Pygments': ['Pygments>=0.6'], + 'reST': ['docutils>=0.3'], + 'SilverCity': ['SilverCity>=0.9.4'], + 'Textile': ['textile>=2.0'], + }, + + entry_points = """ + [console_scripts] + trac-admin = trac.admin.console:run + tracd = trac.web.standalone:main + + [trac.plugins] + trac.about = trac.about + trac.admin.console = trac.admin.console + trac.admin.web_ui = trac.admin.web_ui + trac.attachment = trac.attachment + trac.db.mysql = trac.db.mysql_backend + trac.db.postgres = trac.db.postgres_backend + trac.db.sqlite = trac.db.sqlite_backend + trac.mimeview.patch = trac.mimeview.patch + trac.mimeview.pygments = trac.mimeview.pygments[Pygments] + trac.mimeview.rst = trac.mimeview.rst[reST] + trac.mimeview.silvercity = trac.mimeview.silvercity[SilverCity] + trac.mimeview.txtl = trac.mimeview.txtl[Textile] + trac.prefs = trac.prefs.web_ui + trac.search = trac.search.web_ui + trac.ticket.admin = trac.ticket.admin + trac.ticket.query = trac.ticket.query + trac.ticket.report = trac.ticket.report + trac.ticket.roadmap = trac.ticket.roadmap + trac.ticket.web_ui = trac.ticket.web_ui + trac.timeline = trac.timeline.web_ui + trac.versioncontrol.admin = trac.versioncontrol.admin + trac.versioncontrol.svn_authz = trac.versioncontrol.svn_authz + trac.versioncontrol.svn_fs = trac.versioncontrol.svn_fs + trac.versioncontrol.svn_prop = trac.versioncontrol.svn_prop + trac.versioncontrol.web_ui = trac.versioncontrol.web_ui + trac.web.auth = trac.web.auth + trac.web.session = trac.web.session + trac.wiki.admin = trac.wiki.admin + trac.wiki.interwiki = trac.wiki.interwiki + trac.wiki.macros = trac.wiki.macros + trac.wiki.web_ui = trac.wiki.web_ui + trac.wiki.web_api = trac.wiki.web_api + tracopt.mimeview.enscript = tracopt.mimeview.enscript + tracopt.mimeview.php = tracopt.mimeview.php + tracopt.perm.authz_policy = tracopt.perm.authz_policy + tracopt.perm.config_perm_provider = tracopt.perm.config_perm_provider + tracopt.ticket.commit_updater = tracopt.ticket.commit_updater + tracopt.ticket.deleter = tracopt.ticket.deleter + """, + + **extra +) diff --git a/src/distutils2/tests/conversions/05_before.py b/src/distutils2/tests/conversions/05_before.py new file mode 100755 --- /dev/null +++ b/src/distutils2/tests/conversions/05_before.py @@ -0,0 +1,137 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright (C) 2003-2009 Edgewall Software +# All rights reserved. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at http://trac.edgewall.org/wiki/TracLicense. +# +# This software consists of voluntary contributions made by many +# individuals. For the exact contribution history, see the revision +# history and logs, available at http://trac.edgewall.org/log/. + +from setuptools import setup, find_packages + +extra = {} + +try: + import babel + + extractors = [ + ('**.py', 'python', None), + ('**/templates/**.html', 'genshi', None), + ('**/templates/**.txt', 'genshi', + {'template_class': 'genshi.template:NewTextTemplate'}), + ] + extra['message_extractors'] = { + 'trac': extractors, + 'tracopt': extractors, + } + + from trac.util.dist import get_l10n_js_cmdclass + extra['cmdclass'] = get_l10n_js_cmdclass() + +except ImportError, e: + pass + +setup( + name = 'Trac', + version = '0.12.1', + description = 'Integrated SCM, wiki, issue tracker and project environment', + long_description = """ +Trac is a minimalistic web-based software project management and bug/issue +tracking system. It provides an interface to the Subversion revision control +systems, an integrated wiki, flexible issue tracking and convenient report +facilities. +""", + author = 'Edgewall Software', + author_email = 'info at edgewall.com', + license = 'BSD', + url = 'http://trac.edgewall.org/', + download_url = 'http://trac.edgewall.org/wiki/TracDownload', + classifiers = [ + 'Environment :: Web Environment', + 'Framework :: Trac', + 'Intended Audience :: Developers', + 'License :: OSI Approved :: BSD License', + 'Operating System :: OS Independent', + 'Programming Language :: Python', + 'Topic :: Software Development :: Bug Tracking', + 'Topic :: Software Development :: Version Control', + ], + + packages = find_packages(exclude=['*.tests']), + package_data = { + '': ['templates/*'], + 'trac': ['htdocs/*.*', 'htdocs/README', 'htdocs/js/*.*', + 'htdocs/js/messages/*.*', 'htdocs/css/*.*', + 'htdocs/guide/*', 'locale/*/LC_MESSAGES/messages.mo'], + 'trac.wiki': ['default-pages/*'], + 'trac.ticket': ['workflows/*.ini'], + }, + + test_suite = 'trac.test.suite', + zip_safe = True, + + install_requires = [ + 'setuptools>=0.6b1', + 'Genshi>=0.6', + ], + extras_require = { + 'Babel': ['Babel>=0.9.5'], + 'Pygments': ['Pygments>=0.6'], + 'reST': ['docutils>=0.3'], + 'SilverCity': ['SilverCity>=0.9.4'], + 'Textile': ['textile>=2.0'], + }, + + entry_points = """ + [console_scripts] + trac-admin = trac.admin.console:run + tracd = trac.web.standalone:main + + [trac.plugins] + trac.about = trac.about + trac.admin.console = trac.admin.console + trac.admin.web_ui = trac.admin.web_ui + trac.attachment = trac.attachment + trac.db.mysql = trac.db.mysql_backend + trac.db.postgres = trac.db.postgres_backend + trac.db.sqlite = trac.db.sqlite_backend + trac.mimeview.patch = trac.mimeview.patch + trac.mimeview.pygments = trac.mimeview.pygments[Pygments] + trac.mimeview.rst = trac.mimeview.rst[reST] + trac.mimeview.silvercity = trac.mimeview.silvercity[SilverCity] + trac.mimeview.txtl = trac.mimeview.txtl[Textile] + trac.prefs = trac.prefs.web_ui + trac.search = trac.search.web_ui + trac.ticket.admin = trac.ticket.admin + trac.ticket.query = trac.ticket.query + trac.ticket.report = trac.ticket.report + trac.ticket.roadmap = trac.ticket.roadmap + trac.ticket.web_ui = trac.ticket.web_ui + trac.timeline = trac.timeline.web_ui + trac.versioncontrol.admin = trac.versioncontrol.admin + trac.versioncontrol.svn_authz = trac.versioncontrol.svn_authz + trac.versioncontrol.svn_fs = trac.versioncontrol.svn_fs + trac.versioncontrol.svn_prop = trac.versioncontrol.svn_prop + trac.versioncontrol.web_ui = trac.versioncontrol.web_ui + trac.web.auth = trac.web.auth + trac.web.session = trac.web.session + trac.wiki.admin = trac.wiki.admin + trac.wiki.interwiki = trac.wiki.interwiki + trac.wiki.macros = trac.wiki.macros + trac.wiki.web_ui = trac.wiki.web_ui + trac.wiki.web_api = trac.wiki.web_api + tracopt.mimeview.enscript = tracopt.mimeview.enscript + tracopt.mimeview.php = tracopt.mimeview.php + tracopt.perm.authz_policy = tracopt.perm.authz_policy + tracopt.perm.config_perm_provider = tracopt.perm.config_perm_provider + tracopt.ticket.commit_updater = tracopt.ticket.commit_updater + tracopt.ticket.deleter = tracopt.ticket.deleter + """, + + **extra +) diff --git a/src/distutils2/tests/test_util.py b/src/distutils2/tests/test_util.py --- a/src/distutils2/tests/test_util.py +++ b/src/distutils2/tests/test_util.py @@ -4,6 +4,8 @@ from copy import copy from StringIO import StringIO import subprocess +import tempfile +import time from distutils2.errors import (DistutilsPlatformError, DistutilsByteCompileError, @@ -257,7 +259,10 @@ def test_newer(self): self.assertRaises(DistutilsFileError, util.newer, 'xxx', 'xxx') - + self.newer_f1 = tempfile.NamedTemporaryFile() + time.sleep(1) + self.newer_f2 = tempfile.NamedTemporaryFile() + self.assertTrue(util.newer(self.newer_f2.name, self.newer_f1.name)) def test_find_packages(self): # let's create a structure we want to scan: diff --git a/src/distutils2/tests/test_version.py b/src/distutils2/tests/test_version.py --- a/src/distutils2/tests/test_version.py +++ b/src/distutils2/tests/test_version.py @@ -3,7 +3,7 @@ import os from distutils2.version import NormalizedVersion as V -from distutils2.version import IrrationalVersionError +from distutils2.version import HugeMajorVersionNumError, IrrationalVersionError from distutils2.version import suggest_normalized_version as suggest from distutils2.version import VersionPredicate from distutils2.tests.support import unittest @@ -22,6 +22,10 @@ (V('1.0.dev345'), '1.0.dev345'), (V('1.0.post456.dev623'), '1.0.post456.dev623')) + def test_repr(self): + + self.assertEqual(repr(V('1.0')), "NormalizedVersion('1.0')") + def test_basic_versions(self): for v, s in self.versions: @@ -44,6 +48,12 @@ for s in irrational: self.assertRaises(IrrationalVersionError, V, s) + def test_huge_version(self): + + self.assertEquals(str(V('1980.0')), '1980.0') + self.assertRaises(HugeMajorVersionNumError, V, '1981.0') + self.assertEquals(str(V('1981.0', error_on_huge_major_num=False)), '1981.0') + def test_comparison(self): r""" >>> V('1.2.0') == '1.2' @@ -51,12 +61,33 @@ ... TypeError: cannot compare NormalizedVersion and str + >>> V('1.2') < '1.3' + Traceback (most recent call last): + ... + TypeError: cannot compare NormalizedVersion and str + >>> V('1.2.0') == V('1.2') True >>> V('1.2.0') == V('1.2.3') False + >>> V('1.2.0') != V('1.2.3') + True >>> V('1.2.0') < V('1.2.3') True + >>> V('1.2.0') < V('1.2.0') + False + >>> V('1.2.0') <= V('1.2.0') + True + >>> V('1.2.0') <= V('1.2.3') + True + >>> V('1.2.3') <= V('1.2.0') + False + >>> V('1.2.0') >= V('1.2.0') + True + >>> V('1.2.3') >= V('1.2.0') + True + >>> V('1.2.0') >= V('1.2.3') + False >>> (V('1.0') > V('1.0b2')) True >>> (V('1.0') > V('1.0c2') > V('1.0c1') > V('1.0b2') > V('1.0b1') @@ -101,6 +132,7 @@ self.assertEqual(suggest('1.0c2'), '1.0c2') self.assertEqual(suggest('walla walla washington'), None) self.assertEqual(suggest('2.4c1'), '2.4c1') + self.assertEqual(suggest('v1.0'), '1.0') # from setuptools self.assertEqual(suggest('0.4a1.r10'), '0.4a1.post10') @@ -151,6 +183,8 @@ self.assertFalse(VersionPredicate('Hey (<=2.5)').match('2.6.0')) self.assertTrue(VersionPredicate('Hey (>=2.5)').match('2.5.1')) + self.assertRaises(ValueError, VersionPredicate, '') + # XXX need to silent the micro version in this case #assert not VersionPredicate('Ho (<3.0,!=2.6)').match('2.6.3') @@ -164,11 +198,21 @@ for version in other_versions: self.assertFalse(V(version).is_final) +class VersionWhiteBoxTestCase(unittest.TestCase): + + def test_parse_numdots(self): + # For code coverage completeness, as pad_zeros_length can't be set or + # influenced from the public interface + self.assertEquals(V('1.0')._parse_numdots('1.0', '1.0', + pad_zeros_length=3), + [1, 0, 0]) + def test_suite(): #README = os.path.join(os.path.dirname(__file__), 'README.txt') #suite = [doctest.DocFileSuite(README), unittest.makeSuite(VersionTestCase)] - suite = [unittest.makeSuite(VersionTestCase)] + suite = [unittest.makeSuite(VersionTestCase), + unittest.makeSuite(VersionWhiteBoxTestCase)] return unittest.TestSuite(suite) if __name__ == "__main__": diff --git a/src/distutils2/version.py b/src/distutils2/version.py --- a/src/distutils2/version.py +++ b/src/distutils2/version.py @@ -379,8 +379,6 @@ def __init__(self, predicate): predicate = predicate.strip() match = _PLAIN_VERSIONS.match(predicate) - if match is None: - raise ValueError('Bad predicate "%s"' % predicate) self.name = None predicates = match.groups()[0] self.predicates = [_split_predicate(pred.strip()) @@ -391,8 +389,6 @@ def __init__(self, predicate): predicate = predicate.strip() match = _PLAIN_VERSIONS.match(predicate) - if match is None: - raise ValueError('Bad predicate "%s"' % predicate) self.name = None self.predicates = _split_predicate(match.groups()[0]) diff --git a/src/runtests-cov.py b/src/runtests-cov.py new file mode 100755 --- /dev/null +++ b/src/runtests-cov.py @@ -0,0 +1,105 @@ +#!/usr/bin/env python +"""Tests for distutils2. + +The tests for distutils2 are defined in the distutils2.tests package. +""" + +# TODO: + +# The coverage report is only accurate when ran inside a virtualenv +# created with the --no-site-packages option. When it's not the case, +# the built-in ignore list is not accurate and third party packages +# show-up in the report, lowering the overall coverage. + +# One particular problem it docutils on Ubuntu which has a __file__ +# starting with /usr/lib/python2.6 while the path in the coverage +# report starts with /usr/share/pyshared. + +import sys +from os.path import dirname +from optparse import OptionParser + +def parse_opts(): + parser = OptionParser(usage="%prog [OPTIONS]", + description="run the distutils2 unittests") + + parser.add_option("-q", "--quiet", help="do not print verbose messages", + action="store_true", default=False) + parser.add_option("-c", "--coverage", action="store_true", default=False, + help="produce a coverage report at the end of the run") + parser.add_option("-r", "--report", action="store_true", default=False, + help="produce a coverage report from the last test run") + parser.add_option("-m", "--show-missing", action="store_true", + default=False, + help=("Show line numbers of statements in each module " + "that weren't executed.")) + + opts, args = parser.parse_args() + return opts, args + +def coverage_report(opts): + import coverage + import unittest2 + import docutils + cov = coverage.coverage() + cov.load() + + cov.report(omit_prefixes=["distutils2/tests", + "runtests", + "distutils2/_backport", + dirname(unittest2.__file__), + dirname(dirname(docutils.__file__))], + show_missing=opts.show_missing) + + +def test_main(): + opts, args = parse_opts() + verbose = not opts.quiet + ret = 0 + + if opts.coverage or opts.report: + import coverage + + if opts.coverage: + cov = coverage.coverage() + cov.erase() + cov.start() + if not opts.report: + ret = run_tests(verbose) + if opts.coverage: + cov.stop() + cov.save() + + if opts.report or opts.coverage: + coverage_report(opts) + + return ret + +def run_tests(verbose): + import distutils2.tests + from distutils2.tests import run_unittest, reap_children, TestFailed + from distutils2._backport.tests import test_suite as btest_suite + # XXX just supporting -q right now to enable detailed/quiet output + if len(sys.argv) > 1: + verbose = sys.argv[-1] != '-q' + else: + verbose = 1 + try: + try: + run_unittest([distutils2.tests.test_suite(), btest_suite()], + verbose_=verbose) + return 0 + except TestFailed: + return 1 + finally: + reap_children() + +if __name__ == "__main__": + try: + from distutils2.tests.support import unittest + except ImportError: + sys.stderr.write('Error: You have to install unittest2') + sys.exit(1) + + sys.exit(test_main()) + -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:47 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:47 +0200 Subject: [Python-checkins] distutils2: Merge upstream. Message-ID: tarek.ziade pushed 919bbd329ab3 to distutils2: http://hg.python.org/distutils2/rev/919bbd329ab3 changeset: 504:919bbd329ab3 parent: 503:3d5db5aa326a parent: 494:292c0b541b47 user: Alexis Metaireau date: Fri Aug 06 17:17:22 2010 +0200 summary: Merge upstream. files: docs/source/new_commands.rst, src/Modules/_hashopenssl.c, src/Modules/md5.c, src/Modules/md5.h, src/Modules/md5module.c, src/Modules/sha256module.c, src/Modules/sha512module.c, src/Modules/shamodule.c, src/distutils2/command/install_egg_info.py, src/distutils2/depgraph.py, src/distutils2/index/dist.py, src/distutils2/index/simple.py, src/distutils2/index/xmlrpc.py, src/distutils2/metadata.py, src/distutils2/tests/pypi_server.py diff --git a/.hgignore b/.hgignore --- a/.hgignore +++ b/.hgignore @@ -1,9 +1,10 @@ syntax: glob *.py[co] __pycache__/ +*.so configure.cache build/ MANIFEST dist/ *.swp -.coverage \ No newline at end of file +.coverage diff --git a/docs/design/pep-0376.txt b/docs/design/pep-0376.txt --- a/docs/design/pep-0376.txt +++ b/docs/design/pep-0376.txt @@ -17,7 +17,7 @@ This PEP proposes various enhancements for Distutils: - A new format for the .egg-info structure. -- Some APIs to read the meta-data of a distribution. +- Some APIs to read the metadata of a distribution. - A replacement PEP 262. - An uninstall feature. diff --git a/docs/source/command_hooks.rst b/docs/source/command_hooks.rst new file mode 100644 --- /dev/null +++ b/docs/source/command_hooks.rst @@ -0,0 +1,31 @@ +============= +Command hooks +============= + +Distutils2 provides a way of extending its commands by the use of pre- and +post- command hooks. The hooks are simple Python functions (or any callable +objects) and are specified in the config file using their full qualified names. +The pre-hooks are run after the command is finalized (its options are +processed), but before it is run. The post-hooks are run after the command +itself. Both types of hooks receive an instance of the command object. + +Sample usage of hooks +===================== + +Firstly, you need to make sure your hook is present in the path. This is usually +done by dropping them to the same folder where `setup.py` file lives :: + + # file: myhooks.py + def my_install_hook(install_cmd): + print "Oh la la! Someone is installing my project!" + +Then, you need to point to it in your `setup.cfg` file, under the appropriate +command section :: + + [install] + pre-hook.project = myhooks.my_install_hook + +The hooks defined in different config files (system-wide, user-wide and +package-wide) do not override each other as long as they are specified with +different aliases (additional names after the dot). The alias in the example +above is ``project``. diff --git a/docs/source/new_commands.rst b/docs/source/commands.rst rename from docs/source/new_commands.rst rename to docs/source/commands.rst --- a/docs/source/new_commands.rst +++ b/docs/source/commands.rst @@ -6,6 +6,50 @@ You might recognize some of them from other projects, like Distribute or Setuptools. +``upload`` - Upload source and/or binary distributions to PyPI +============================================================== + +The Python Package Index (PyPI) not only stores the package info, but also the +package data if the author of the package wishes to. The distutils command +:command:`upload` pushes the distribution files to PyPI. + +The command is invoked immediately after building one or more distribution +files. For example, the command :: + + python setup.py sdist bdist_wininst upload + +will cause the source distribution and the Windows installer to be uploaded to +PyPI. Note that these will be uploaded even if they are built using an earlier +invocation of :file:`setup.py`, but that only distributions named on the command +line for the invocation including the :command:`upload` command are uploaded. + +The :command:`upload` command uses the username, password, and repository URL +from the :file:`$HOME/.pypirc` file . If a :command:`register` command was +previously called in the same command, and if the password was entered in the +prompt, :command:`upload` will reuse the entered password. This is useful if +you do not want to store a clear text password in the :file:`$HOME/.pypirc` +file. + +The ``upload`` command has a few options worth noting: + +``--sign, -s`` + Sign each uploaded file using GPG (GNU Privacy Guard). The ``gpg`` program + must be available for execution on the system ``PATH``. + +``--identity=NAME, -i NAME`` + Specify the identity or key name for GPG to use when signing. The value of + this option will be passed through the ``--local-user`` option of the + ``gpg`` program. + +``--show-response`` + Display the full response text from server; this is useful for debugging + PyPI problems. + +``--repository=URL, -r URL`` + The URL of the repository to upload to. Defaults to + http://pypi.python.org/pypi (i.e., the main PyPI installation). + + ``upload_docs`` - Upload package documentation to PyPI ====================================================== @@ -40,7 +84,7 @@ python setup.py upload_docs --upload-dir=docs/build/html -As with any other ``setuptools`` based command, you can define useful +As with any other command, you can define useful defaults in the ``setup.cfg`` of your Python project, e.g.: .. code-block:: ini diff --git a/docs/source/index.rst b/docs/source/index.rst --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -14,7 +14,8 @@ metadata pkgutil depgraph - new_commands + commands + command_hooks test_framework projects-index version diff --git a/docs/source/pkgutil.rst b/docs/source/pkgutil.rst --- a/docs/source/pkgutil.rst +++ b/docs/source/pkgutil.rst @@ -17,6 +17,17 @@ first a complete documentation of the functions and classes is provided and then several use cases are presented. +Caching ++++++++ + +For performance purposes, the list of distributions is being internally +cached. It is enabled by default, but you can turn it off or clear +it using +:func:`distutils2._backport.pkgutil.enable_cache`, +:func:`distutils2._backport.pkgutil.disable_cache` and +:func:`distutils2._backport.pkgutil.clear_cache`. + + API Reference ============= @@ -48,7 +59,7 @@ print('=====') for (path, md5, size) in dist.get_installed_files(): print('* Path: %s' % path) - print(' Hash %s, Size: %s bytes' % (md5, size)) + print(' Hash %s, Size: %s bytes' % (md5, size)) print('Metadata') print('========') for key, value in dist.metadata.items(): diff --git a/src/DEVNOTES.txt b/src/DEVNOTES.txt --- a/src/DEVNOTES.txt +++ b/src/DEVNOTES.txt @@ -1,10 +1,21 @@ Notes for developers ==================== -- Distutils2 runs from 2.4 to 3.2 (3.x not implemented yet), so - make sure you don't use a syntax that doesn't work under +- Distutils2 runs on Python from 2.4 to 3.2 (3.x not implemented yet), + so make sure you don't use a syntax that doesn't work under one of these Python versions. - Always run tests.sh before you push a change. This implies - that you have all Python versions installed from 2.4 to 2.6. + that you have all Python versions installed from 2.4 to 2.7. +- With Python 2.4, if you want to run tests with runtests.py, or run + just one test directly, be sure to run python2.4 setup.py build_ext + first, else tests won't find _hashlib or _md5. When using tests.sh, + build_ext is automatically done. + +- Renaming to do: + + - DistributionMetadata > Metadata or ReleaseMetadata + - pkgutil > pkgutil.__init__ + new pkgutil.database (or better name) + - RationalizedVersion > Version + - suggest_rationalized_version > suggest diff --git a/src/distutils2/__init__.py b/src/distutils2/__init__.py --- a/src/distutils2/__init__.py +++ b/src/distutils2/__init__.py @@ -20,7 +20,7 @@ __version__ = "1.0a2" -# when set to True, converts doctests by default too +# when set to True, converts doctests by default too run_2to3_on_doctests = True # Standard package names for fixer packages lib2to3_fixer_packages = ['lib2to3.fixes'] diff --git a/src/Modules/_hashopenssl.c b/src/distutils2/_backport/_hashopenssl.c rename from src/Modules/_hashopenssl.c rename to src/distutils2/_backport/_hashopenssl.c diff --git a/src/distutils2/_backport/hashlib.py b/src/distutils2/_backport/hashlib.py --- a/src/distutils2/_backport/hashlib.py +++ b/src/distutils2/_backport/hashlib.py @@ -65,20 +65,20 @@ def __get_builtin_constructor(name): if name in ('SHA1', 'sha1'): - import _sha + from distutils2._backport import _sha return _sha.new elif name in ('MD5', 'md5'): - import _md5 + from distutils2._backport import _md5 return _md5.new elif name in ('SHA256', 'sha256', 'SHA224', 'sha224'): - import _sha256 + from distutils2._backport import _sha256 bs = name[3:] if bs == '256': return _sha256.sha256 elif bs == '224': return _sha256.sha224 elif name in ('SHA512', 'sha512', 'SHA384', 'sha384'): - import _sha512 + from distutils2._backport import _sha512 bs = name[3:] if bs == '512': return _sha512.sha512 @@ -122,7 +122,7 @@ try: - import _hashlib + from distutils2._backport import _hashlib new = __hash_new __get_hash = __get_openssl_constructor except ImportError: diff --git a/src/Modules/md5.c b/src/distutils2/_backport/md5.c rename from src/Modules/md5.c rename to src/distutils2/_backport/md5.c diff --git a/src/Modules/md5.h b/src/distutils2/_backport/md5.h rename from src/Modules/md5.h rename to src/distutils2/_backport/md5.h diff --git a/src/Modules/md5module.c b/src/distutils2/_backport/md5module.c rename from src/Modules/md5module.c rename to src/distutils2/_backport/md5module.c diff --git a/src/distutils2/_backport/pkgutil.py b/src/distutils2/_backport/pkgutil.py --- a/src/distutils2/_backport/pkgutil.py +++ b/src/distutils2/_backport/pkgutil.py @@ -20,6 +20,7 @@ import re import warnings + __all__ = [ 'get_importer', 'iter_importers', 'get_loader', 'find_loader', 'walk_packages', 'iter_modules', 'get_data', @@ -27,6 +28,7 @@ 'Distribution', 'EggInfoDistribution', 'distinfo_dirname', 'get_distributions', 'get_distribution', 'get_file_users', 'provides_distribution', 'obsoletes_distribution', + 'enable_cache', 'disable_cache', 'clear_cache' ] @@ -187,8 +189,8 @@ searches the current ``sys.path``, plus any modules that are frozen or built-in. - Note that :class:`ImpImporter` does not currently support being used by placement - on ``sys.meta_path``. + Note that :class:`ImpImporter` does not currently support being used by + placement on ``sys.meta_path``. """ def __init__(self, path=None): @@ -577,7 +579,8 @@ argument should be the name of a package, in standard module format (``foo.bar``). The resource argument should be in the form of a relative filename, using ``'/'`` as the path separator. The parent directory name - ``'..'`` is not allowed, and nor is a rooted name (starting with a ``'/'``). + ``'..'`` is not allowed, and nor is a rooted name (starting with a + ``'/'``). The function returns a binary string, which is the contents of the specified resource. @@ -613,6 +616,97 @@ DIST_FILES = ('INSTALLER', 'METADATA', 'RECORD', 'REQUESTED',) +# Cache +_cache_name = {} # maps names to Distribution instances +_cache_name_egg = {} # maps names to EggInfoDistribution instances +_cache_path = {} # maps paths to Distribution instances +_cache_path_egg = {} # maps paths to EggInfoDistribution instances +_cache_generated = False # indicates if .dist-info distributions are cached +_cache_generated_egg = False # indicates if .dist-info and .egg are cached +_cache_enabled = True + + +def enable_cache(): + """ + Enables the internal cache. + + Note that this function will not clear the cache in any case, for that + functionality see :func:`clear_cache`. + """ + global _cache_enabled + + _cache_enabled = True + +def disable_cache(): + """ + Disables the internal cache. + + Note that this function will not clear the cache in any case, for that + functionality see :func:`clear_cache`. + """ + global _cache_enabled + + _cache_enabled = False + +def clear_cache(): + """ Clears the internal cache. """ + global _cache_name, _cache_name_egg, cache_path, _cache_path_egg, \ + _cache_generated, _cache_generated_egg + + _cache_name = {} + _cache_name_egg = {} + _cache_path = {} + _cache_path_egg = {} + _cache_generated = False + _cache_generated_egg = False + + +def _yield_distributions(include_dist, include_egg): + """ + Yield .dist-info and .egg(-info) distributions, based on the arguments + + :parameter include_dist: yield .dist-info distributions + :parameter include_egg: yield .egg(-info) distributions + """ + for path in sys.path: + realpath = os.path.realpath(path) + if not os.path.isdir(realpath): + continue + for dir in os.listdir(realpath): + dist_path = os.path.join(realpath, dir) + if include_dist and dir.endswith('.dist-info'): + yield Distribution(dist_path) + elif include_egg and (dir.endswith('.egg-info') or + dir.endswith('.egg')): + yield EggInfoDistribution(dist_path) + + +def _generate_cache(use_egg_info=False): + global _cache_generated, _cache_generated_egg + + if _cache_generated_egg or (_cache_generated and not use_egg_info): + return + else: + gen_dist = not _cache_generated + gen_egg = use_egg_info + + for dist in _yield_distributions(gen_dist, gen_egg): + if isinstance(dist, Distribution): + _cache_path[dist.path] = dist + if not dist.name in _cache_name: + _cache_name[dist.name] = [] + _cache_name[dist.name].append(dist) + else: + _cache_path_egg[dist.path] = dist + if not dist.name in _cache_name_egg: + _cache_name_egg[dist.name] = [] + _cache_name_egg[dist.name].append(dist) + + if gen_dist: + _cache_generated = True + if gen_egg: + _cache_generated_egg = True + class Distribution(object): """Created with the *path* of the ``.dist-info`` directory provided to the @@ -627,15 +721,23 @@ """A :class:`distutils2.metadata.DistributionMetadata` instance loaded with the distribution's ``METADATA`` file.""" requested = False - """A boolean that indicates whether the ``REQUESTED`` metadata file is present - (in other words, whether the package was installed by user request).""" + """A boolean that indicates whether the ``REQUESTED`` metadata file is + present (in other words, whether the package was installed by user + request or it was installed as a dependency).""" def __init__(self, path): + if _cache_enabled and path in _cache_path: + self.metadata = _cache_path[path].metadata + else: + metadata_path = os.path.join(path, 'METADATA') + self.metadata = DistributionMetadata(path=metadata_path) + self.path = path - metadata_path = os.path.join(path, 'METADATA') - self.metadata = DistributionMetadata(path=metadata_path) self.name = self.metadata['name'] + if _cache_enabled and not path in _cache_path: + _cache_path[path] = self + def _get_records(self, local=False): RECORD = os.path.join(self.path, 'RECORD') record_reader = csv_reader(open(RECORD, 'rb'), delimiter=',') @@ -756,6 +858,11 @@ def __init__(self, path): self.path = path + if _cache_enabled and path in _cache_path_egg: + self.metadata = _cache_path_egg[path].metadata + self.name = self.metadata['Name'] + return + # reused from Distribute's pkg_resources def yield_lines(strs): """Yield non-empty/non-comment lines of a ``basestring`` or sequence""" @@ -787,7 +894,7 @@ requires = zipf.get_data('EGG-INFO/requires.txt') except IOError: requires = None - self.name = self.metadata['name'] + self.name = self.metadata['Name'] elif path.endswith('.egg-info'): if os.path.isdir(path): path = os.path.join(path, 'PKG-INFO') @@ -840,6 +947,9 @@ else: self.metadata['Requires'] += reqs + if _cache_enabled: + _cache_path_egg[self.path] = self + def get_installed_files(self, local=False): return [] @@ -898,17 +1008,17 @@ :rtype: iterator of :class:`Distribution` and :class:`EggInfoDistribution` instances""" - for path in sys.path: - realpath = os.path.realpath(path) - if not os.path.isdir(realpath): - continue - for dir in os.listdir(realpath): - if dir.endswith('.dist-info'): - dist = Distribution(os.path.join(realpath, dir)) - yield dist - elif use_egg_info and (dir.endswith('.egg-info') or - dir.endswith('.egg')): - dist = EggInfoDistribution(os.path.join(realpath, dir)) + if not _cache_enabled: + for dist in _yield_distributions(True, use_egg_info): + yield dist + else: + _generate_cache(use_egg_info) + + for dist in _cache_path.itervalues(): + yield dist + + if use_egg_info: + for dist in _cache_path_egg.itervalues(): yield dist @@ -928,17 +1038,19 @@ value is expected. If the directory is not found, ``None`` is returned. :rtype: :class:`Distribution` or :class:`EggInfoDistribution` or None""" - found = None - for dist in get_distributions(): - if dist.name == name: - found = dist - break - if use_egg_info: - for dist in get_distributions(True): + if not _cache_enabled: + for dist in _yield_distributions(True, use_egg_info): if dist.name == name: - found = dist - break - return found + return dist + else: + _generate_cache(use_egg_info) + + if name in _cache_name: + return _cache_name[name][0] + elif use_egg_info and name in _cache_name_egg: + return _cache_name_egg[name][0] + else: + return None def obsoletes_distribution(name, version=None, use_egg_info=False): diff --git a/src/Modules/sha256module.c b/src/distutils2/_backport/sha256module.c rename from src/Modules/sha256module.c rename to src/distutils2/_backport/sha256module.c diff --git a/src/Modules/sha512module.c b/src/distutils2/_backport/sha512module.c rename from src/Modules/sha512module.c rename to src/distutils2/_backport/sha512module.c diff --git a/src/Modules/shamodule.c b/src/distutils2/_backport/shamodule.c rename from src/Modules/shamodule.c rename to src/distutils2/_backport/shamodule.c diff --git a/src/distutils2/_backport/tarfile.py b/src/distutils2/_backport/tarfile.py --- a/src/distutils2/_backport/tarfile.py +++ b/src/distutils2/_backport/tarfile.py @@ -56,13 +56,6 @@ if not hasattr(os, 'SEEK_SET'): os.SEEK_SET = 0 -if sys.platform == 'mac': - # This module needs work for MacOS9, especially in the area of pathname - # handling. In many places it is assumed a simple substitution of / by the - # local os.path.sep is good enough to convert pathnames, but this does not - # work with the mac rooted:path:name versus :nonrooted:path:name syntax - raise ImportError, "tarfile does not work for platform==mac" - try: import grp, pwd except ImportError: diff --git a/src/distutils2/_backport/tests/__init__.py b/src/distutils2/_backport/tests/__init__.py --- a/src/distutils2/_backport/tests/__init__.py +++ b/src/distutils2/_backport/tests/__init__.py @@ -4,7 +4,7 @@ from distutils2.tests.support import unittest -here = os.path.dirname(__file__) +here = os.path.dirname(__file__) or os.curdir def test_suite(): suite = unittest.TestSuite() @@ -16,4 +16,5 @@ suite.addTest(module.test_suite()) return suite - +if __name__ == '__main__': + unittest.main(defaultTest='test_suite') diff --git a/src/distutils2/_backport/tests/fake_dists/grammar-1.0a4.dist-info/METADATA b/src/distutils2/_backport/tests/fake_dists/grammar-1.0a4.dist-info/METADATA --- a/src/distutils2/_backport/tests/fake_dists/grammar-1.0a4.dist-info/METADATA +++ b/src/distutils2/_backport/tests/fake_dists/grammar-1.0a4.dist-info/METADATA @@ -2,3 +2,4 @@ Name: grammar Version: 1.0a4 Requires-Dist: truffles (>=1.2) +Author: Sherlock Holmes diff --git a/src/distutils2/_backport/tests/fake_dists/truffles-5.0.egg-info b/src/distutils2/_backport/tests/fake_dists/truffles-5.0.egg-info new file mode 100644 --- /dev/null +++ b/src/distutils2/_backport/tests/fake_dists/truffles-5.0.egg-info @@ -0,0 +1,3 @@ +Metadata-Version: 1.2 +Name: truffles +Version: 5.0 diff --git a/src/distutils2/_backport/tests/test_pkgutil.py b/src/distutils2/_backport/tests/test_pkgutil.py --- a/src/distutils2/_backport/tests/test_pkgutil.py +++ b/src/distutils2/_backport/tests/test_pkgutil.py @@ -10,7 +10,7 @@ try: from hashlib import md5 except ImportError: - from md5 import md5 + from distutils2._backport.hashlib import md5 from test.test_support import run_unittest, TESTFN from distutils2.tests.support import unittest @@ -25,12 +25,14 @@ except ImportError: from unittest2.compatibility import relpath +# Adapted from Python 2.7's trunk + # TODO Add a test for getting a distribution that is provided by another # distribution. # TODO Add a test for absolute pathed RECORD items (e.g. /etc/myapp/config.ini) -# Adapted from Python 2.7's trunk + class TestPkgUtilData(unittest.TestCase): def setUp(self): @@ -108,10 +110,14 @@ del sys.modules[pkg] + # Adapted from Python 2.7's trunk + + class TestPkgUtilPEP302(unittest.TestCase): class MyTestLoader(object): + def load_module(self, fullname): # Create an empty module mod = sys.modules.setdefault(fullname, imp.new_module(fullname)) @@ -120,13 +126,14 @@ # Make it a package mod.__path__ = [] # Count how many times the module is reloaded - mod.__dict__['loads'] = mod.__dict__.get('loads',0) + 1 + mod.__dict__['loads'] = mod.__dict__.get('loads', 0) + 1 return mod def get_data(self, path): return "Hello, world!" class MyTestImporter(object): + def find_module(self, fullname, path=None): return TestPkgUtilPEP302.MyTestLoader() @@ -319,7 +326,7 @@ current_path = os.path.abspath(os.path.dirname(__file__)) self.sys_path = sys.path[:] self.fake_dists_path = os.path.join(current_path, 'fake_dists') - sys.path[0:0] = [self.fake_dists_path] + sys.path.insert(0, self.fake_dists_path) def tearDown(self): sys.path[:] = self.sys_path @@ -366,8 +373,12 @@ if not isinstance(dist, Distribution): self.fail("item received was not a Distribution instance: " "%s" % type(dist)) - if dist.name in dict(fake_dists).keys(): + if dist.name in dict(fake_dists).keys() and \ + dist.path.startswith(self.fake_dists_path): found_dists.append((dist.name, dist.metadata['version'],)) + else: + # check that it doesn't find anything more than this + self.assertFalse(dist.path.startswith(self.fake_dists_path)) # otherwise we don't care what other distributions are found # Finally, test that we found all that we were looking for @@ -375,7 +386,8 @@ # Now, test if the egg-info distributions are found correctly as well fake_dists += [('bacon', '0.1'), ('cheese', '2.0.2'), - ('banana', '0.4'), ('strawberry', '0.6')] + ('banana', '0.4'), ('strawberry', '0.6'), + ('truffles', '5.0')] found_dists = [] dists = [dist for dist in get_distributions(use_egg_info=True)] @@ -384,8 +396,11 @@ isinstance(dist, EggInfoDistribution)): self.fail("item received was not a Distribution or " "EggInfoDistribution instance: %s" % type(dist)) - if dist.name in dict(fake_dists).keys(): + if dist.name in dict(fake_dists).keys() and \ + dist.path.startswith(self.fake_dists_path): found_dists.append((dist.name, dist.metadata['version'])) + else: + self.assertFalse(dist.path.startswith(self.fake_dists_path)) self.assertListEqual(sorted(fake_dists), sorted(found_dists)) @@ -485,7 +500,7 @@ l = [dist.name for dist in provides_distribution('truffles', '>1.5', use_egg_info=True)] - checkLists(l, ['bacon']) + checkLists(l, ['bacon', 'truffles']) l = [dist.name for dist in provides_distribution('truffles', '>=1.0')] checkLists(l, ['choxie', 'towel-stuff']) @@ -549,6 +564,33 @@ l = [dist.name for dist in obsoletes_distribution('truffles', '0.2')] checkLists(l, ['towel-stuff']) + def test_yield_distribution(self): + # tests the internal function _yield_distributions + from distutils2._backport.pkgutil import _yield_distributions + checkLists = lambda x, y: self.assertListEqual(sorted(x), sorted(y)) + + eggs = [('bacon', '0.1'), ('banana', '0.4'), ('strawberry', '0.6'), + ('truffles', '5.0'), ('cheese', '2.0.2')] + dists = [('choxie', '2.0.0.9'), ('grammar', '1.0a4'), + ('towel-stuff', '0.1')] + + checkLists([], _yield_distributions(False, False)) + + found = [(dist.name, dist.metadata['Version']) + for dist in _yield_distributions(False, True) + if dist.path.startswith(self.fake_dists_path)] + checkLists(eggs, found) + + found = [(dist.name, dist.metadata['Version']) + for dist in _yield_distributions(True, False) + if dist.path.startswith(self.fake_dists_path)] + checkLists(dists, found) + + found = [(dist.name, dist.metadata['Version']) + for dist in _yield_distributions(True, True) + if dist.path.startswith(self.fake_dists_path)] + checkLists(dists + eggs, found) + def test_suite(): suite = unittest.TestSuite() diff --git a/src/distutils2/command/bdist_dumb.py b/src/distutils2/command/bdist_dumb.py --- a/src/distutils2/command/bdist_dumb.py +++ b/src/distutils2/command/bdist_dumb.py @@ -77,9 +77,7 @@ ("don't know how to create dumb built distributions " + "on platform %s") % os.name - self.set_undefined_options('bdist', - ('dist_dir', 'dist_dir'), - ('plat_name', 'plat_name')) + self.set_undefined_options('bdist', 'dist_dir', 'plat_name') def run(self): if not self.skip_build: diff --git a/src/distutils2/command/bdist_msi.py b/src/distutils2/command/bdist_msi.py --- a/src/distutils2/command/bdist_msi.py +++ b/src/distutils2/command/bdist_msi.py @@ -153,10 +153,7 @@ else: self.versions = list(self.all_versions) - self.set_undefined_options('bdist', - ('dist_dir', 'dist_dir'), - ('plat_name', 'plat_name'), - ) + self.set_undefined_options('bdist', 'dist_dir', 'plat_name') if self.pre_install_script: raise DistutilsOptionError, "the pre-install-script feature is not yet implemented" diff --git a/src/distutils2/command/bdist_wininst.py b/src/distutils2/command/bdist_wininst.py --- a/src/distutils2/command/bdist_wininst.py +++ b/src/distutils2/command/bdist_wininst.py @@ -100,10 +100,7 @@ " option must be specified" % (short_version,) self.target_version = short_version - self.set_undefined_options('bdist', - ('dist_dir', 'dist_dir'), - ('plat_name', 'plat_name'), - ) + self.set_undefined_options('bdist', 'dist_dir', 'plat_name') if self.install_script: for script in self.distribution.scripts: @@ -177,8 +174,8 @@ # And make an archive relative to the root of the # pseudo-installation tree. - from tempfile import mktemp - archive_basename = mktemp() + from tempfile import NamedTemporaryFile + archive_basename = NamedTemporaryFile().name fullname = self.distribution.get_fullname() arcname = self.make_archive(archive_basename, "zip", root_dir=self.bdist_dir) diff --git a/src/distutils2/command/build_clib.py b/src/distutils2/command/build_clib.py --- a/src/distutils2/command/build_clib.py +++ b/src/distutils2/command/build_clib.py @@ -76,9 +76,7 @@ self.set_undefined_options('build', ('build_temp', 'build_clib'), ('build_temp', 'build_temp'), - ('compiler', 'compiler'), - ('debug', 'debug'), - ('force', 'force')) + 'compiler', 'debug', 'force') self.libraries = self.distribution.libraries if self.libraries: diff --git a/src/distutils2/command/build_ext.py b/src/distutils2/command/build_ext.py --- a/src/distutils2/command/build_ext.py +++ b/src/distutils2/command/build_ext.py @@ -177,13 +177,8 @@ def finalize_options(self): self.set_undefined_options('build', - ('build_lib', 'build_lib'), - ('build_temp', 'build_temp'), - ('compiler', 'compiler'), - ('debug', 'debug'), - ('force', 'force'), - ('plat_name', 'plat_name'), - ) + 'build_lib', 'build_temp', 'compiler', + 'debug', 'force', 'plat_name') if self.package is None: self.package = self.distribution.ext_package @@ -292,7 +287,7 @@ "config")) else: # building python standard extensions - self.library_dirs.append('.') + self.library_dirs.append(os.curdir) # for extensions under Linux or Solaris with a shared Python library, # Python's library directory must be appended to library_dirs @@ -305,7 +300,7 @@ self.library_dirs.append(sysconfig.get_config_var('LIBDIR')) else: # building python standard extensions - self.library_dirs.append('.') + self.library_dirs.append(os.curdir) # The argument parsing will result in self.define being a string, but # it has to be a list of 2-tuples. All the preprocessor symbols diff --git a/src/distutils2/command/build_py.py b/src/distutils2/command/build_py.py --- a/src/distutils2/command/build_py.py +++ b/src/distutils2/command/build_py.py @@ -95,9 +95,7 @@ self._doctests_2to3 = [] def finalize_options(self): - self.set_undefined_options('build', - ('build_lib', 'build_lib'), - ('force', 'force')) + self.set_undefined_options('build', 'build_lib', 'force') # Get the distribution options that are aliases for build_py # options -- list of packages and list of modules. diff --git a/src/distutils2/command/build_scripts.py b/src/distutils2/command/build_scripts.py --- a/src/distutils2/command/build_scripts.py +++ b/src/distutils2/command/build_scripts.py @@ -40,8 +40,7 @@ def finalize_options (self): self.set_undefined_options('build', ('build_scripts', 'build_dir'), - ('force', 'force'), - ('executable', 'executable')) + 'force', 'executable') self.scripts = self.distribution.scripts def get_source_files(self): diff --git a/src/distutils2/command/check.py b/src/distutils2/command/check.py --- a/src/distutils2/command/check.py +++ b/src/distutils2/command/check.py @@ -8,13 +8,13 @@ from distutils2.errors import DistutilsSetupError class check(Command): - """This command checks the meta-data of the package. + """This command checks the metadata of the package. """ description = ("perform some checks on the package") - user_options = [('metadata', 'm', 'Verify meta-data'), + user_options = [('metadata', 'm', 'Verify metadata'), ('restructuredtext', 'r', - ('Checks if long string meta-data syntax ' - 'are reStructuredText-compliant')), + ('Checks if long string metadata syntax ' + 'is reStructuredText-compliant')), ('strict', 's', 'Will exit with an error if a check fails')] @@ -53,7 +53,7 @@ raise DistutilsSetupError(msg) def check_metadata(self): - """Ensures that all required elements of meta-data are supplied. + """Ensures that all required elements of metadata are supplied. name, version, URL, (author and author_email) or (maintainer and maintainer_email)). @@ -62,7 +62,7 @@ """ missing, __ = self.distribution.metadata.check() if missing != []: - self.warn("missing required meta-data: %s" % ', '.join(missing)) + self.warn("missing required metadata: %s" % ', '.join(missing)) def check_restructuredtext(self): """Checks if the long string fields are reST-compliant.""" diff --git a/src/distutils2/command/clean.py b/src/distutils2/command/clean.py --- a/src/distutils2/command/clean.py +++ b/src/distutils2/command/clean.py @@ -40,13 +40,9 @@ self.all = None def finalize_options(self): - self.set_undefined_options('build', - ('build_base', 'build_base'), - ('build_lib', 'build_lib'), - ('build_scripts', 'build_scripts'), - ('build_temp', 'build_temp')) - self.set_undefined_options('bdist', - ('bdist_base', 'bdist_base')) + self.set_undefined_options('build', 'build_base', 'build_lib', + 'build_scripts', 'build_temp') + self.set_undefined_options('bdist', 'bdist_base') def run(self): # remove the build/temp. directory (unless it's already diff --git a/src/distutils2/command/cmd.py b/src/distutils2/command/cmd.py --- a/src/distutils2/command/cmd.py +++ b/src/distutils2/command/cmd.py @@ -51,6 +51,12 @@ # defined. The canonical example is the "install" command. sub_commands = [] + # Pre and post command hooks are run just before or just after the command + # itself. They are simple functions that receive the command instance. They + # should be specified as dotted strings. + pre_hook = None + post_hook = None + # -- Creation/initialization methods ------------------------------- @@ -297,26 +303,30 @@ else: return self.__class__.__name__ - def set_undefined_options(self, src_cmd, *option_pairs): - """Set the values of any "undefined" options from corresponding - option values in some other command object. "Undefined" here means - "is None", which is the convention used to indicate that an option - has not been changed between 'initialize_options()' and - 'finalize_options()'. Usually called from 'finalize_options()' for - options that depend on some other command rather than another - option of the same command. 'src_cmd' is the other command from - which option values will be taken (a command object will be created - for it if necessary); the remaining arguments are - '(src_option,dst_option)' tuples which mean "take the value of - 'src_option' in the 'src_cmd' command object, and copy it to - 'dst_option' in the current command object". + def set_undefined_options(self, src_cmd, *options): + """Set values of undefined options from another command. + + Undefined options are options set to None, which is the convention + used to indicate that an option has not been changed between + 'initialize_options()' and 'finalize_options()'. This method is + usually called from 'finalize_options()' for options that depend on + some other command rather than another option of the same command, + typically subcommands. + + The 'src_cmd' argument is the other command from which option values + will be taken (a command object will be created for it if necessary); + the remaining positional arguments are strings that give the name of + the option to set. If the name is different on the source and target + command, you can pass a tuple with '(name_on_source, name_on_dest)' so + that 'self.name_on_dest' will be set from 'src_cmd.name_on_source'. """ - - # Option_pairs: list of (src_option, dst_option) tuples - src_cmd_obj = self.distribution.get_command_obj(src_cmd) src_cmd_obj.ensure_finalized() - for (src_option, dst_option) in option_pairs: + for obj in options: + if isinstance(obj, tuple): + src_option, dst_option = obj + else: + src_option, dst_option = obj, obj if getattr(self, dst_option) is None: setattr(self, dst_option, getattr(src_cmd_obj, src_option)) diff --git a/src/distutils2/command/install.py b/src/distutils2/command/install.py --- a/src/distutils2/command/install.py +++ b/src/distutils2/command/install.py @@ -79,15 +79,23 @@ ('record=', None, "filename in which to record list of installed files"), + + # .dist-info related arguments, read by install_dist_info + ('no-distinfo', None, 'do not create a .dist-info directory'), + ('installer=', None, 'the name of the installer'), + ('requested', None, 'generate a REQUESTED file'), + ('no-requested', None, 'do not generate a REQUESTED file'), + ('no-record', None, 'do not generate a RECORD file'), ] - boolean_options = ['compile', 'force', 'skip-build'] + boolean_options = ['compile', 'force', 'skip-build', 'no-dist-info', + 'requested', 'no-dist-record',] user_options.append(('user', None, "install in user site-package '%s'" % \ get_path('purelib', '%s_user' % os.name))) boolean_options.append('user') - negative_opt = {'no-compile' : 'compile'} + negative_opt = {'no-compile' : 'compile', 'no-requested': 'requested'} def initialize_options(self): @@ -160,6 +168,12 @@ self.record = None + # .dist-info related options + self.no_distinfo = None + self.installer = None + self.requested = None + self.no_record = None + # -- Option finalizing methods ------------------------------------- # (This is rather more involved than for most commands, @@ -299,13 +313,14 @@ self.dump_dirs("after prepending root") # Find out the build directories, ie. where to install from. - self.set_undefined_options('build', - ('build_base', 'build_base'), - ('build_lib', 'build_lib')) + self.set_undefined_options('build', 'build_base', 'build_lib') # Punt on doc directories for now -- after all, we're punting on # documentation completely! + if self.no_distinfo is None: + self.no_distinfo = False + def dump_dirs(self, msg): """Dumps the list of user options.""" from distutils2.fancy_getopt import longopt_xlate @@ -586,5 +601,7 @@ ('install_headers', has_headers), ('install_scripts', has_scripts), ('install_data', has_data), - ('install_egg_info', lambda self:True), + # keep install_distinfo last, as it needs the record + # with files to be completely generated + ('install_distinfo', lambda self: not self.no_distinfo), ] diff --git a/src/distutils2/command/install_data.py b/src/distutils2/command/install_data.py --- a/src/distutils2/command/install_data.py +++ b/src/distutils2/command/install_data.py @@ -37,9 +37,7 @@ def finalize_options(self): self.set_undefined_options('install', ('install_data', 'install_dir'), - ('root', 'root'), - ('force', 'force'), - ) + 'root', 'force') def run(self): self.mkpath(self.install_dir) diff --git a/src/distutils2/command/install_distinfo.py b/src/distutils2/command/install_distinfo.py new file mode 100644 --- /dev/null +++ b/src/distutils2/command/install_distinfo.py @@ -0,0 +1,175 @@ +""" +distutils.command.install_distinfo +================================== + +:Author: Josip Djolonga + +This module implements the ``install_distinfo`` command that creates the +``.dist-info`` directory for the distribution, as specified in :pep:`376`. +Usually, you do not have to call this command directly, it gets called +automatically by the ``install`` command. +""" + +# This file was created from the code for the former command install_egg_info + +import os +import csv +import re +from distutils2.command.cmd import Command +from distutils2 import log +from distutils2._backport.shutil import rmtree +try: + import hashlib +except ImportError: + from distutils2._backport import hashlib + + +class install_distinfo(Command): + """Install a .dist-info directory for the package""" + + description = 'Install a .dist-info directory for the package' + + user_options = [ + ('distinfo-dir=', None, + 'directory where the the .dist-info directory will ' + 'be installed'), + ('installer=', None, 'the name of the installer'), + ('requested', None, 'generate a REQUESTED file'), + ('no-requested', None, 'do not generate a REQUESTED file'), + ('no-record', None, 'do not generate a RECORD file'), + ] + + boolean_options = [ + 'requested', + 'no-record', + ] + + negative_opt = {'no-requested': 'requested'} + + def initialize_options(self): + self.distinfo_dir = None + self.installer = None + self.requested = None + self.no_record = None + + def finalize_options(self): + self.set_undefined_options('install', + ('installer', 'installer'), + ('requested', 'requested'), + ('no_record', 'no_record')) + + self.set_undefined_options('install_lib', + ('install_dir', 'distinfo_dir')) + + if self.installer is None: + self.installer = 'distutils' + if self.requested is None: + self.requested = True + if self.no_record is None: + self.no_record = False + + metadata = self.distribution.metadata + + basename = "%s-%s.dist-info" % ( + to_filename(safe_name(metadata['Name'])), + to_filename(safe_version(metadata['Version'])), + ) + + self.distinfo_dir = os.path.join(self.distinfo_dir, basename) + self.outputs = [] + + def run(self): + if not self.dry_run: + target = self.distinfo_dir + + if os.path.isdir(target) and not os.path.islink(target): + rmtree(target) + elif os.path.exists(target): + self.execute(os.unlink, (self.distinfo_dir,), + "Removing " + target) + + self.execute(os.makedirs, (target,), "Creating " + target) + + metadata_path = os.path.join(self.distinfo_dir, 'METADATA') + log.info('Creating %s' % (metadata_path,)) + self.distribution.metadata.write(metadata_path) + self.outputs.append(metadata_path) + + installer_path = os.path.join(self.distinfo_dir, 'INSTALLER') + log.info('Creating %s' % (installer_path,)) + f = open(installer_path, 'w') + try: + f.write(self.installer) + finally: + f.close() + self.outputs.append(installer_path) + + if self.requested: + requested_path = os.path.join(self.distinfo_dir, 'REQUESTED') + log.info('Creating %s' % (requested_path,)) + f = open(requested_path, 'w') + f.close() + self.outputs.append(requested_path) + + if not self.no_record: + record_path = os.path.join(self.distinfo_dir, 'RECORD') + log.info('Creating %s' % (record_path,)) + f = open(record_path, 'wb') + try: + writer = csv.writer(f, delimiter=',', + lineterminator=os.linesep, + quotechar='"') + + install = self.get_finalized_command('install') + + for fpath in install.get_outputs(): + if fpath.endswith('.pyc') or fpath.endswith('.pyo'): + # do not put size and md5 hash, as in PEP-376 + writer.writerow((fpath, '', '')) + else: + size = os.path.getsize(fpath) + fd = open(fpath, 'r') + hash = hashlib.md5() + hash.update(fd.read()) + md5sum = hash.hexdigest() + writer.writerow((fpath, md5sum, size)) + + # add the RECORD file itself + writer.writerow((record_path, '', '')) + self.outputs.append(record_path) + finally: + f.close() + + def get_outputs(self): + return self.outputs + + +# The following routines are taken from setuptools' pkg_resources module and +# can be replaced by importing them from pkg_resources once it is included +# in the stdlib. + + +def safe_name(name): + """Convert an arbitrary string to a standard distribution name + + Any runs of non-alphanumeric/. characters are replaced with a single '-'. + """ + return re.sub('[^A-Za-z0-9.]+', '-', name) + + +def safe_version(version): + """Convert an arbitrary string to a standard version string + + Spaces become dots, and all other non-alphanumeric characters become + dashes, with runs of multiple dashes condensed to a single dash. + """ + version = version.replace(' ', '.') + return re.sub('[^A-Za-z0-9.]+', '-', version) + + +def to_filename(name): + """Convert a project or version name to its filename-escaped form + + Any '-' characters are currently replaced with '_'. + """ + return name.replace('-', '_') diff --git a/src/distutils2/command/install_egg_info.py b/src/distutils2/command/install_egg_info.py deleted file mode 100644 --- a/src/distutils2/command/install_egg_info.py +++ /dev/null @@ -1,83 +0,0 @@ -"""distutils.command.install_egg_info - -Implements the Distutils 'install_egg_info' command, for installing -a package's PKG-INFO metadata.""" - - -from distutils2.command.cmd import Command -from distutils2 import log -from distutils2._backport.shutil import rmtree -import os, sys, re - -class install_egg_info(Command): - """Install an .egg-info file for the package""" - - description = "Install package's PKG-INFO metadata as an .egg-info file" - user_options = [ - ('install-dir=', 'd', "directory to install to"), - ] - - def initialize_options(self): - self.install_dir = None - - def finalize_options(self): - metadata = self.distribution.metadata - self.set_undefined_options('install_lib',('install_dir','install_dir')) - basename = "%s-%s-py%s.egg-info" % ( - to_filename(safe_name(metadata['Name'])), - to_filename(safe_version(metadata['Version'])), - sys.version[:3] - ) - self.target = os.path.join(self.install_dir, basename) - self.outputs = [self.target] - - def run(self): - target = self.target - if os.path.isdir(target) and not os.path.islink(target): - if self.dry_run: - pass # XXX - else: - rmtree(target) - elif os.path.exists(target): - self.execute(os.unlink,(self.target,),"Removing "+target) - elif not os.path.isdir(self.install_dir): - self.execute(os.makedirs, (self.install_dir,), - "Creating "+self.install_dir) - log.info("Writing %s", target) - if not self.dry_run: - f = open(target, 'w') - self.distribution.metadata.write_file(f) - f.close() - - def get_outputs(self): - return self.outputs - - -# The following routines are taken from setuptools' pkg_resources module and -# can be replaced by importing them from pkg_resources once it is included -# in the stdlib. - -def safe_name(name): - """Convert an arbitrary string to a standard distribution name - - Any runs of non-alphanumeric/. characters are replaced with a single '-'. - """ - return re.sub('[^A-Za-z0-9.]+', '-', name) - - -def safe_version(version): - """Convert an arbitrary string to a standard version string - - Spaces become dots, and all other non-alphanumeric characters become - dashes, with runs of multiple dashes condensed to a single dash. - """ - version = version.replace(' ','.') - return re.sub('[^A-Za-z0-9.]+', '-', version) - - -def to_filename(name): - """Convert a project or version name to its filename-escaped form - - Any '-' characters are currently replaced with '_'. - """ - return name.replace('-','_') diff --git a/src/distutils2/command/install_headers.py b/src/distutils2/command/install_headers.py --- a/src/distutils2/command/install_headers.py +++ b/src/distutils2/command/install_headers.py @@ -29,8 +29,7 @@ def finalize_options(self): self.set_undefined_options('install', ('install_headers', 'install_dir'), - ('force', 'force')) - + 'force') def run(self): headers = self.distribution.headers diff --git a/src/distutils2/command/install_lib.py b/src/distutils2/command/install_lib.py --- a/src/distutils2/command/install_lib.py +++ b/src/distutils2/command/install_lib.py @@ -68,11 +68,7 @@ self.set_undefined_options('install', ('build_lib', 'build_dir'), ('install_lib', 'install_dir'), - ('force', 'force'), - ('compile', 'compile'), - ('optimize', 'optimize'), - ('skip_build', 'skip_build'), - ) + 'force', 'compile', 'optimize', 'skip_build') if self.compile is None: self.compile = 1 diff --git a/src/distutils2/command/install_scripts.py b/src/distutils2/command/install_scripts.py --- a/src/distutils2/command/install_scripts.py +++ b/src/distutils2/command/install_scripts.py @@ -36,9 +36,7 @@ self.set_undefined_options('build', ('build_scripts', 'build_dir')) self.set_undefined_options('install', ('install_scripts', 'install_dir'), - ('force', 'force'), - ('skip_build', 'skip_build'), - ) + 'force', 'skip_build') def run (self): if not self.skip_build: diff --git a/src/distutils2/command/register.py b/src/distutils2/command/register.py --- a/src/distutils2/command/register.py +++ b/src/distutils2/command/register.py @@ -21,15 +21,16 @@ class register(Command): - description = ("register the distribution with the Python package index") - user_options = [('repository=', 'r', - "url of repository [default: %s]" % DEFAULT_REPOSITORY), + description = "register the distribution with the Python package index" + user_options = [ + ('repository=', 'r', + "repository URL [default: %s]" % DEFAULT_REPOSITORY), ('show-response', None, - 'display full response text from server'), + "display full response text from server"), ('list-classifiers', None, - 'list the valid Trove classifiers'), + "list valid Trove classifiers"), ('strict', None , - 'Will stop the registering if the meta-data are not fully compliant') + "stop the registration if the metadata is not fully compliant") ] boolean_options = ['show-response', 'verify', 'list-classifiers', diff --git a/src/distutils2/command/sdist.py b/src/distutils2/command/sdist.py --- a/src/distutils2/command/sdist.py +++ b/src/distutils2/command/sdist.py @@ -70,8 +70,8 @@ ('dist-dir=', 'd', "directory to put the source distribution archive(s) in " "[default: dist]"), - ('medata-check', None, - "Ensure that all required elements of meta-data " + ('check-metadata', None, + "Ensure that all required elements of metadata " "are supplied. Warn if any missing. [default]"), ('owner=', 'u', "Owner name used when creating a tar file [default: current user]"), @@ -80,7 +80,7 @@ ] boolean_options = ['use-defaults', 'prune', - 'manifest-only', 'keep-temp', 'metadata-check'] + 'manifest-only', 'keep-temp', 'check-metadata'] help_options = [ ('help-formats', None, @@ -362,7 +362,7 @@ 'self.keep_temp' is true). The list of archive files created is stored so it can be retrieved later by 'get_archive_files()'. """ - # Don't warn about missing meta-data here -- should be (and is!) + # Don't warn about missing metadata here -- should be (and is!) # done elsewhere. base_dir = self.distribution.get_fullname() base_name = os.path.join(self.dist_dir, base_dir) diff --git a/src/distutils2/command/upload.py b/src/distutils2/command/upload.py --- a/src/distutils2/command/upload.py +++ b/src/distutils2/command/upload.py @@ -11,7 +11,7 @@ try: from hashlib import md5 except ImportError: - from md5 import md5 + from distutils2._backport.hashlib import md5 from distutils2.errors import DistutilsOptionError from distutils2.util import spawn @@ -24,16 +24,19 @@ class upload(Command): - description = "upload binary package to PyPI" + description = "upload distribution to PyPI" - user_options = [('repository=', 'r', - "url of repository [default: %s]" % \ - DEFAULT_REPOSITORY), + user_options = [ + ('repository=', 'r', + "repository URL [default: %s]" % DEFAULT_REPOSITORY), ('show-response', None, - 'display full response text from server'), + "display full response text from server"), ('sign', 's', - 'sign files to upload using gpg'), - ('identity=', 'i', 'GPG identity used to sign files'), + "sign files to upload using gpg"), + ('identity=', 'i', + "GPG identity used to sign files"), + ('upload-docs', None, + "upload documentation too"), ] boolean_options = ['show-response', 'sign'] @@ -47,6 +50,7 @@ self.show_response = 0 self.sign = False self.identity = None + self.upload_docs = False def finalize_options(self): if self.repository is None: @@ -74,6 +78,12 @@ raise DistutilsOptionError("No dist file created in earlier command") for command, pyversion, filename in self.distribution.dist_files: self.upload_file(command, pyversion, filename) + if self.upload_docs: + upload_docs = self.get_finalized_command("upload_docs") + upload_docs.repository = self.repository + upload_docs.username = self.username + upload_docs.password = self.password + upload_docs.run() # XXX to be refactored with register.post_to_server def upload_file(self, command, pyversion, filename): @@ -94,7 +104,7 @@ spawn(gpg_args, dry_run=self.dry_run) - # Fill in the data - send all the meta-data in case we need to + # Fill in the data - send all the metadata in case we need to # register a new release content = open(filename,'rb').read() diff --git a/src/distutils2/command/upload_docs.py b/src/distutils2/command/upload_docs.py --- a/src/distutils2/command/upload_docs.py +++ b/src/distutils2/command/upload_docs.py @@ -52,16 +52,19 @@ class upload_docs(Command): user_options = [ - ('repository=', 'r', "url of repository [default: %s]" % DEFAULT_REPOSITORY), - ('show-response', None, 'display full response text from server'), - ('upload-dir=', None, 'directory to upload'), + ('repository=', 'r', + "repository URL [default: %s]" % DEFAULT_REPOSITORY), + ('show-response', None, + "display full response text from server"), + ('upload-dir=', None, + "directory to upload"), ] def initialize_options(self): self.repository = None self.realm = None self.show_response = 0 - self.upload_dir = "build/docs" + self.upload_dir = None self.username = '' self.password = '' @@ -70,7 +73,7 @@ self.repository = DEFAULT_REPOSITORY if self.realm is None: self.realm = DEFAULT_REALM - if self.upload_dir == None: + if self.upload_dir is None: build = self.get_finalized_command('build') self.upload_dir = os.path.join(build.build_base, "docs") self.announce('Using upload directory %s' % self.upload_dir) diff --git a/src/distutils2/core.py b/src/distutils2/core.py --- a/src/distutils2/core.py +++ b/src/distutils2/core.py @@ -33,6 +33,7 @@ or: %(script)s cmd --help """ + def gen_usage(script_name): script = os.path.basename(script_name) return USAGE % {'script': script} @@ -59,6 +60,7 @@ 'extra_objects', 'extra_compile_args', 'extra_link_args', 'swig_opts', 'export_symbols', 'depends', 'language') + def setup(**attrs): """The gateway to the Distutils: do everything your setup script needs to do, in a highly flexible and user-driven way. Briefly: create a @@ -155,7 +157,7 @@ def run_setup(script_name, script_args=None, stop_after="run"): """Run a setup script in a somewhat controlled environment, and return the Distribution instance that drives things. This is useful - if you need to find out the distribution meta-data (passed as + if you need to find out the distribution metadata (passed as keyword args from 'script' to 'setup()', or the contents of the config files or command-line. @@ -205,8 +207,6 @@ # Hmm, should we do something if exiting with a non-zero code # (ie. error)? pass - except: - raise if _setup_distribution is None: raise RuntimeError, \ diff --git a/src/distutils2/depgraph.py b/src/distutils2/depgraph.py --- a/src/distutils2/depgraph.py +++ b/src/distutils2/depgraph.py @@ -1,5 +1,5 @@ -"""Analyse the relationships between the distributions in the system and generate -a dependency graph. +"""Analyse the relationships between the distributions in the system +and generate a dependency graph. """ from distutils2.errors import DistutilsError diff --git a/src/distutils2/dist.py b/src/distutils2/dist.py --- a/src/distutils2/dist.py +++ b/src/distutils2/dist.py @@ -6,19 +6,17 @@ __revision__ = "$Id: dist.py 77717 2010-01-24 00:33:32Z tarek.ziade $" -import sys, os, re - -try: - import warnings -except ImportError: - warnings = None +import sys +import os +import re +import warnings from ConfigParser import RawConfigParser from distutils2.errors import (DistutilsOptionError, DistutilsArgError, DistutilsModuleError, DistutilsClassError) from distutils2.fancy_getopt import FancyGetopt, translate_longopt -from distutils2.util import check_environ, strtobool +from distutils2.util import check_environ, strtobool, resolve_dotted_name from distutils2 import log from distutils2.metadata import DistributionMetadata @@ -26,7 +24,7 @@ # the same as a Python NAME -- I don't allow leading underscores. The fact # that they're very similar is no coincidence; the default naming scheme is # to look for a Python module named after the command. -command_re = re.compile (r'^[a-zA-Z]([a-zA-Z0-9_]*)$') +command_re = re.compile(r'^[a-zA-Z]([a-zA-Z0-9_]*)$') class Distribution(object): @@ -43,7 +41,6 @@ See the code for 'setup()', in core.py, for details. """ - # 'global_options' describes the command-line options that may be # supplied to the setup script prior to any actual commands. # Eg. "./setup.py -n" or "./setup.py --quiet" both take advantage of @@ -116,7 +113,7 @@ ('use-2to3', None, "use 2to3 to make source python 3.x compatible"), ('convert-2to3-doctests', None, - "use 2to3 to convert doctests in seperate text files"), + "use 2to3 to convert doctests in seperate text files"), ] display_option_names = map(lambda x: translate_longopt(x[0]), display_options) @@ -124,10 +121,8 @@ # negative options are options that exclude other options negative_opt = {'quiet': 'verbose'} - # -- Creation/initialization methods ------------------------------- - - def __init__ (self, attrs=None): + def __init__(self, attrs=None): """Construct a new Distribution instance: initialize all the attributes of a Distribution, and then use 'attrs' (a dictionary mapping attribute names to values) to assign some of those @@ -145,17 +140,12 @@ for attr in self.display_option_names: setattr(self, attr, 0) - # Store the distribution meta-data (name, version, author, and so + # Store the distribution metadata (name, version, author, and so # forth) in a separate object -- we're getting to have enough # information here (and enough command-line options) that it's - # worth it. Also delegate 'get_XXX()' methods to the 'metadata' - # object in a sneaky and underhanded (but efficient!) way. + # worth it. self.metadata = DistributionMetadata() - #for basename in self.metadata._METHOD_BASENAMES: - # method_name = "get_" + basename - # setattr(self, method_name, getattr(self.metadata, method_name)) - # 'cmdclass' maps command names to class objects, so we # can 1) quickly figure out which class to instantiate when # we need to create a new command object, and 2) have a way @@ -257,10 +247,7 @@ setattr(self, key, val) else: msg = "Unknown distribution option: %r" % key - if warnings is not None: - warnings.warn(msg) - else: - sys.stderr.write(msg + "\n") + warnings.warn(msg) # no-user-cfg is handled before other command line args # because other args override the config files, and this @@ -286,10 +273,10 @@ and return the new dictionary; otherwise, return the existing option dictionary. """ - dict = self.command_options.get(command) - if dict is None: - dict = self.command_options[command] = {} - return dict + d = self.command_options.get(command) + if d is None: + d = self.command_options[command] = {} + return d def get_fullname(self): return self.metadata.get_fullname() @@ -385,9 +372,21 @@ for opt in options: if opt != '__name__': - val = parser.get(section,opt) + val = parser.get(section, opt) opt = opt.replace('-', '_') - opt_dict[opt] = (filename, val) + + # ... although practicality beats purity :( + if opt.startswith("pre_hook.") or opt.startswith("post_hook."): + hook_type, alias = opt.split(".") + # Hooks are somewhat exceptional, as they are + # gathered from many config files (not overriden as + # other options). + # The option_dict expects {"command": ("filename", # "value")} + # so for hooks, we store only the last config file processed + hook_dict = opt_dict.setdefault(hook_type, (filename, {}))[1] + hook_dict[alias] = val + else: + opt_dict[opt] = (filename, val) # Make the RawConfigParser forget everything (so we retain # the original filenames that options come from) @@ -402,12 +401,12 @@ try: if alias: setattr(self, alias, not strtobool(val)) - elif opt in ('verbose', 'dry_run'): # ugh! + elif opt in ('verbose', 'dry_run'): # ugh! setattr(self, opt, strtobool(val)) else: setattr(self, opt, val) except ValueError, msg: - raise DistutilsOptionError, msg + raise DistutilsOptionError(msg) # -- Command-line parsing methods ---------------------------------- @@ -473,7 +472,7 @@ # Oops, no commands found -- an end-user error if not self.commands: - raise DistutilsArgError, "no commands supplied" + raise DistutilsArgError("no commands supplied") # All is well: return true return 1 @@ -504,7 +503,7 @@ # Pull the current command from the head of the command line command = args[0] if not command_re.match(command): - raise SystemExit, "invalid command name '%s'" % command + raise SystemExit("invalid command name '%s'" % command) self.commands.append(command) # Dig up the command class that implements this command, so we @@ -513,22 +512,21 @@ try: cmd_class = self.get_command_class(command) except DistutilsModuleError, msg: - raise DistutilsArgError, msg + raise DistutilsArgError(msg) # Require that the command class be derived from Command -- want # to be sure that the basic "command" interface is implemented. if not issubclass(cmd_class, Command): - raise DistutilsClassError, \ - "command class %s must subclass Command" % cmd_class + raise DistutilsClassError( + "command class %s must subclass Command" % cmd_class) # Also make sure that the command object provides a list of its # known options. if not (hasattr(cmd_class, 'user_options') and isinstance(cmd_class.user_options, list)): - raise DistutilsClassError, \ - ("command class %s must provide " + - "'user_options' attribute (a list of tuples)") % \ - cmd_class + raise DistutilsClassError( + ("command class %s must provide " + "'user_options' attribute (a list of tuples)") % cmd_class) # If the command class has a list of negative alias options, # merge it in with the global negative aliases. @@ -545,7 +543,6 @@ else: help_options = [] - # All commands support the global options too, just by adding # in 'global_options'. parser.set_option_table(self.global_options + @@ -559,10 +556,10 @@ if (hasattr(cmd_class, 'help_options') and isinstance(cmd_class.help_options, list)): - help_option_found=0 + help_option_found = 0 for (help_option, short, desc, func) in cmd_class.help_options: if hasattr(opts, parser.get_attr_name(help_option)): - help_option_found=1 + help_option_found = 1 if hasattr(func, '__call__'): func() else: @@ -588,7 +585,7 @@ objects. """ if getattr(self, 'convert_2to3_doctests', None): - self.convert_2to3_doctests = [os.path.join(p) + self.convert_2to3_doctests = [os.path.join(p) for p in self.convert_2to3_doctests] else: self.convert_2to3_doctests = [] @@ -801,7 +798,7 @@ class_name = command try: - __import__ (module_name) + __import__(module_name) module = sys.modules[module_name] except ImportError: continue @@ -809,16 +806,15 @@ try: cls = getattr(module, class_name) except AttributeError: - raise DistutilsModuleError, \ - "invalid command '%s' (no class '%s' in module '%s')" \ - % (command, class_name, module_name) + raise DistutilsModuleError( + "invalid command '%s' (no class '%s' in module '%s')" % + (command, class_name, module_name)) self.cmdclass[command] = cls return cls raise DistutilsModuleError("invalid command '%s'" % command) - def get_command_obj(self, command, create=1): """Return the command object for 'command'. Normally this object is cached on a previous call to 'get_command_obj()'; if no command @@ -881,11 +877,11 @@ elif hasattr(command_obj, option): setattr(command_obj, option, value) else: - raise DistutilsOptionError, \ - ("error in %s: command '%s' has no such option '%s'" - % (source, command_name, option)) + raise DistutilsOptionError( + "error in %s: command '%s' has no such option '%s'" % + (source, command_name, option)) except ValueError, msg: - raise DistutilsOptionError, msg + raise DistutilsOptionError(msg) def get_reinitialized_command(self, command, reinit_subcommands=0): """Reinitializes a command to the state it was in when first @@ -953,15 +949,23 @@ if self.have_run.get(command): return - log.info("running %s", command) cmd_obj = self.get_command_obj(command) cmd_obj.ensure_finalized() + self.run_command_hooks(cmd_obj, 'pre_hook') + log.info("running %s", command) cmd_obj.run() + self.run_command_hooks(cmd_obj, 'post_hook') self.have_run[command] = 1 + def run_command_hooks(self, cmd_obj, hook_kind): + hooks = getattr(cmd_obj, hook_kind) + if hooks is None: + return + for hook in hooks.values(): + hook_func = resolve_dotted_name(hook) + hook_func(cmd_obj) # -- Distribution query methods ------------------------------------ - def has_pure_modules(self): return len(self.packages or self.py_modules or []) > 0 @@ -988,13 +992,8 @@ not self.has_ext_modules() and not self.has_c_libraries()) - # -- Metadata query methods ---------------------------------------- - # If you're looking for 'get_name()', 'get_version()', and so forth, - # they are defined in a sneaky way: the constructor binds self.get_XXX - # to self.metadata.get_XXX. The actual code is in the - # DistributionMetadata class, below. - +# XXX keep for compat or remove? def fix_help_options(options): """Convert a 4-tuple 'help_options' list as found in various command diff --git a/src/distutils2/errors.py b/src/distutils2/errors.py --- a/src/distutils2/errors.py +++ b/src/distutils2/errors.py @@ -10,31 +10,38 @@ __revision__ = "$Id: errors.py 75901 2009-10-28 06:45:18Z tarek.ziade $" + class DistutilsError(Exception): """The root of all Distutils evil.""" + class DistutilsModuleError(DistutilsError): """Unable to load an expected module, or to find an expected class within some module (in particular, command modules and classes).""" + class DistutilsClassError(DistutilsError): """Some command class (or possibly distribution class, if anyone feels a need to subclass Distribution) is found not to be holding up its end of the bargain, ie. implementing some part of the "command "interface.""" + class DistutilsGetoptError(DistutilsError): """The option table provided to 'fancy_getopt()' is bogus.""" + class DistutilsArgError(DistutilsError): """Raised by fancy_getopt in response to getopt.error -- ie. an error in the command line usage.""" + class DistutilsFileError(DistutilsError): """Any problems in the filesystem: expected file not found, etc. Typically this is for problems that we detect before IOError or OSError could be raised.""" + class DistutilsOptionError(DistutilsError): """Syntactic/semantic errors in command options, such as use of mutually conflicting options, or inconsistent options, @@ -43,60 +50,76 @@ files, or what-have-you -- but if we *know* something originated in the setup script, we'll raise DistutilsSetupError instead.""" + class DistutilsSetupError(DistutilsError): """For errors that can be definitely blamed on the setup script, such as invalid keyword arguments to 'setup()'.""" + class DistutilsPlatformError(DistutilsError): """We don't know how to do something on the current platform (but we do know how to do it on some platform) -- eg. trying to compile C files on a platform not supported by a CCompiler subclass.""" + class DistutilsExecError(DistutilsError): """Any problems executing an external program (such as the C compiler, when compiling C files).""" + class DistutilsInternalError(DistutilsError): """Internal inconsistencies or impossibilities (obviously, this should never be seen if the code is working!).""" + class DistutilsTemplateError(DistutilsError): """Syntax error in a file list template.""" + class DistutilsByteCompileError(DistutilsError): """Byte compile error.""" + # Exception classes used by the CCompiler implementation classes class CCompilerError(Exception): """Some compile/link operation failed.""" + class PreprocessError(CCompilerError): """Failure to preprocess one or more C/C++ files.""" + class CompileError(CCompilerError): """Failure to compile one or more C/C++ source files.""" + class LibError(CCompilerError): """Failure to create a static library from one or more C/C++ object files.""" + class LinkError(CCompilerError): """Failure to link one or more C/C++ object files into an executable or shared library file.""" + class UnknownFileError(CCompilerError): """Attempt to process an unknown file type.""" + class MetadataConflictError(DistutilsError): """Attempt to read or write metadata fields that are conflictual.""" + class MetadataUnrecognizedVersionError(DistutilsError): """Unknown metadata version number.""" + class IrrationalVersionError(Exception): """This is an irrational version.""" pass + class HugeMajorVersionNumError(IrrationalVersionError): """An irrational version because the major version number is huge (often because a year or date was used). @@ -105,4 +128,3 @@ This guard can be disabled by setting that option False. """ pass - diff --git a/src/distutils2/extension.py b/src/distutils2/extension.py --- a/src/distutils2/extension.py +++ b/src/distutils2/extension.py @@ -17,6 +17,7 @@ # import that large-ish module (indirectly, through distutils.core) in # order to do anything. + class Extension(object): """Just a collection of attributes that describes an extension module and everything needed to build it (hopefully in a portable @@ -84,7 +85,7 @@ # When adding arguments to this constructor, be sure to update # setup_keywords in core.py. - def __init__ (self, name, sources, + def __init__(self, name, sources, include_dirs=None, define_macros=None, undef_macros=None, @@ -95,11 +96,11 @@ extra_compile_args=None, extra_link_args=None, export_symbols=None, - swig_opts = None, + swig_opts=None, depends=None, language=None, optional=None, - **kw # To catch unknown keywords + **kw # To catch unknown keywords ): if not isinstance(name, str): raise AssertionError("'name' must be a string") @@ -134,4 +135,3 @@ options = ', '.join(sorted(options)) msg = "Unknown Extension options: %s" % options warnings.warn(msg) - diff --git a/src/distutils2/fancy_getopt.py b/src/distutils2/fancy_getopt.py --- a/src/distutils2/fancy_getopt.py +++ b/src/distutils2/fancy_getopt.py @@ -30,6 +30,7 @@ # (for use as attributes of some object). longopt_xlate = string.maketrans('-', '_') + class FancyGetopt(object): """Wrapper around the standard 'getopt()' module that provides some handy extra functionality: @@ -42,7 +43,7 @@ on the command line sets 'verbose' to false """ - def __init__ (self, option_table=None): + def __init__(self, option_table=None): # The option table is (currently) a list of tuples. The # tuples may have 3 or four values: @@ -180,7 +181,8 @@ self.long_opts.append(long) if long[-1] == '=': # option takes an argument? - if short: short = short + ':' + if short: + short = short + ':' long = long[0:-1] self.takes_arg[long] = 1 else: diff --git a/src/distutils2/index/simple.py b/src/distutils2/index/simple.py --- a/src/distutils2/index/simple.py +++ b/src/distutils2/index/simple.py @@ -25,7 +25,7 @@ from distutils2.version import get_version_predicate from distutils2 import __version__ as __distutils2_version__ -__all__ = ['Crawler', 'DEFAULT_SIMPLE_INDEX_URL'] +__all__ = ['Crawler', 'DEFAULT_SIMPLE_INDEX_URL'] # -- Constants ----------------------------------------------- DEFAULT_SIMPLE_INDEX_URL = "http://a.pypi.python.org/simple/" @@ -154,7 +154,7 @@ matching_projects.append(self._get_project(project_name)) return matching_projects - def get_releases(self, requirements, prefer_final=None, + def get_releases(self, requirements, prefer_final=None, force_update=False): """Search for releases and return a ReleaseList object containing the results. @@ -167,7 +167,7 @@ if not self._projects.has_key(predicate.name.lower()): raise ProjectNotFound() - + releases = self._projects.get(predicate.name.lower()) releases.sort_releases(prefer_final=prefer_final) return releases @@ -260,7 +260,7 @@ else: name = release_info['name'] if not name.lower() in self._projects: - self._projects[name.lower()] = ReleasesList(name, + self._projects[name.lower()] = ReleasesList(name, index=self._index) if release: diff --git a/src/distutils2/index/wrapper.py b/src/distutils2/index/wrapper.py --- a/src/distutils2/index/wrapper.py +++ b/src/distutils2/index/wrapper.py @@ -3,7 +3,7 @@ _WRAPPER_MAPPINGS = {'get_release': 'simple', 'get_releases': 'simple', - 'search_projects': 'simple', + 'search_projects': 'simple', 'get_metadata': 'xmlrpc', 'get_distributions': 'simple'} @@ -45,7 +45,7 @@ :param index: tell wich index to rely on by default. :param index_classes: a dict of name:class to use as indexes. - :param indexes: a dict of name:index already instanciated + :param indexes: a dict of name:index already instantiated :param mappings: the mappings to use for this wrapper """ @@ -56,7 +56,7 @@ self._indexes = indexes self._default_index = default_index - # instanciate the classes and set their _project attribute to the one + # instantiate the classes and set their _project attribute to the one # of the wrapper. for name, cls in index_classes.items(): obj = self._indexes.setdefault(name, cls()) @@ -77,10 +77,10 @@ # the method is not defined in the mappings, so we try first to get # it via the default index, and rely on others if needed. try: - real_method = getattr(self._indexes[self._default_index], + real_method = getattr(self._indexes[self._default_index], method_name) except AttributeError: - other_indexes = [i for i in self._indexes + other_indexes = [i for i in self._indexes if i != self._default_index] for index in other_indexes: real_method = getattr(self._indexes[index], method_name, None) @@ -90,4 +90,4 @@ return switch_index_if_fails(real_method, self) else: raise AttributeError("No index have attribute '%s'" % method_name) - + diff --git a/src/distutils2/manifest.py b/src/distutils2/manifest.py --- a/src/distutils2/manifest.py +++ b/src/distutils2/manifest.py @@ -22,7 +22,7 @@ # a \ followed by some spaces + EOL _COLLAPSE_PATTERN = re.compile('\\\w*\n', re.M) -_COMMENTED_LINE = re.compile('#.*?(?=\n)|^\w*\n|\n(?=$)', re.M|re.S) +_COMMENTED_LINE = re.compile('#.*?(?=\n)|^\w*\n|\n(?=$)', re.M | re.S) class Manifest(object): """A list of files built by on exploring the filesystem and filtered by diff --git a/src/distutils2/metadata.py b/src/distutils2/metadata.py --- a/src/distutils2/metadata.py +++ b/src/distutils2/metadata.py @@ -1,13 +1,12 @@ -""" -Implementation of the Metadata for Python packages +"""Implementation of the Metadata for Python packages PEPs. -Supports all Metadata formats (1.0, 1.1, 1.2). +Supports all metadata formats (1.0, 1.1, 1.2). """ -import re import os import sys import platform +import re from StringIO import StringIO from email import message_from_file from tokenize import tokenize, NAME, OP, STRING, ENDMARKER @@ -53,12 +52,12 @@ PKG_INFO_PREFERRED_VERSION = '1.0' _LINE_PREFIX = re.compile('\n \|') -_241_FIELDS = ('Metadata-Version', 'Name', 'Version', 'Platform', +_241_FIELDS = ('Metadata-Version', 'Name', 'Version', 'Platform', 'Summary', 'Description', 'Keywords', 'Home-page', 'Author', 'Author-email', 'License') -_314_FIELDS = ('Metadata-Version', 'Name', 'Version', 'Platform', +_314_FIELDS = ('Metadata-Version', 'Name', 'Version', 'Platform', 'Supported-Platform', 'Summary', 'Description', 'Keywords', 'Home-page', 'Author', 'Author-email', 'License', 'Classifier', 'Download-URL', 'Obsoletes', @@ -66,7 +65,7 @@ _314_MARKERS = ('Obsoletes', 'Provides', 'Requires') -_345_FIELDS = ('Metadata-Version', 'Name', 'Version', 'Platform', +_345_FIELDS = ('Metadata-Version', 'Name', 'Version', 'Platform', 'Supported-Platform', 'Summary', 'Description', 'Keywords', 'Home-page', 'Author', 'Author-email', 'Maintainer', 'Maintainer-email', 'License', @@ -78,12 +77,11 @@ 'Obsoletes-Dist', 'Requires-External', 'Maintainer', 'Maintainer-email', 'Project-URL') -_ALL_FIELDS = [] +_ALL_FIELDS = set() +_ALL_FIELDS.update(_241_FIELDS) +_ALL_FIELDS.update(_314_FIELDS) +_ALL_FIELDS.update(_345_FIELDS) -for field in _241_FIELDS + _314_FIELDS + _345_FIELDS: - if field in _ALL_FIELDS: - continue - _ALL_FIELDS.append(field) def _version2fieldlist(version): if version == '1.0': @@ -94,8 +92,9 @@ return _345_FIELDS raise MetadataUnrecognizedVersionError(version) + def _best_version(fields): - """Will detect the best version depending on the fields used.""" + """Detect the best version depending on the fields used.""" def _has_marker(keys, markers): for marker in markers: if marker in keys: @@ -182,23 +181,32 @@ class DistributionMetadata(object): - """Distribution meta-data class (1.0 or 1.2). + """The metadata of a release. - if from_dict attribute is set, all key/values pairs will be sent to the - "set" method, building the metadata from the dict. + Supports versions 1.0, 1.1 and 1.2 (auto-detected). You can + instantiate the class with one of these arguments (or none): + - *path*, the path to a METADATA file + - *fileobj* give a file-like object with METADATA as content + - *mapping* is a dict-like object """ + # TODO document that execution_context and platform_dependent are used + # to filter on query, not when setting a key + # also document the mapping API and UNKNOWN default key + def __init__(self, path=None, platform_dependent=False, execution_context=None, fileobj=None, mapping=None): self._fields = {} self.version = None self.docutils_support = _HAS_DOCUTILS self.platform_dependent = platform_dependent + self.execution_context = execution_context + if [path, fileobj, mapping].count(None) < 2: + raise TypeError('path, fileobj and mapping are exclusive') if path is not None: self.read(path) elif fileobj is not None: self.read_file(fileobj) - self.execution_context = execution_context - if mapping: + elif mapping is not None: self.update(mapping) def _set_best_version(self): @@ -208,10 +216,6 @@ def _write_field(self, file, name, value): file.write('%s: %s\n' % (name, value)) - def _write_list(self, file, name, values): - for value in values: - self._write_field(file, name, value) - def _encode_field(self, value): if isinstance(value, unicode): return value.encode(PKG_INFO_ENCODING) @@ -231,17 +235,15 @@ if name in _ALL_FIELDS: return name name = name.replace('-', '_').lower() - if name in _ATTR2FIELD: - return _ATTR2FIELD[name] - return name + return _ATTR2FIELD.get(name, name) def _default_value(self, name): - if name in _LISTFIELDS + _ELEMENTSFIELD: + if name in _LISTFIELDS or name in _ELEMENTSFIELD: return [] return 'UNKNOWN' def _check_rst_data(self, data): - """Returns warnings when the provided data doesn't compile.""" + """Return warnings when the provided data has syntax errors.""" source_path = StringIO() parser = Parser() settings = frontend.OptionParser().get_default_values() @@ -276,7 +278,7 @@ return _LINE_PREFIX.sub('\n', value) # - # Public APIs + # Public API # def get_fullname(self): return '%s-%s' % (self['Name'], self['Version']) @@ -289,7 +291,7 @@ self.read_file(open(filepath)) def read_file(self, fileob): - """Reads the metadata values from a file object.""" + """Read the metadata values from a file object.""" msg = message_from_file(fileob) self.version = msg['metadata-version'] @@ -307,8 +309,7 @@ self.set(field, value) def write(self, filepath): - """Write the metadata fields into path. - """ + """Write the metadata fields to filepath.""" pkg_info = open(filepath, 'w') try: self.write_file(pkg_info) @@ -316,8 +317,7 @@ pkg_info.close() def write_file(self, fileobject): - """Write the PKG-INFO format data to a file object. - """ + """Write the PKG-INFO format data to a file object.""" self._set_best_version() for field in _version2fieldlist(self.version): values = self.get(field) @@ -368,17 +368,17 @@ self.update(kwargs) def set(self, name, value): - """Controls then sets a metadata field""" + """Control then set a metadata field.""" name = self._convert_name(name) - if (name in _ELEMENTSFIELD + ('Platform',) and + if ((name in _ELEMENTSFIELD or name == 'Platform') and not isinstance(value, (list, tuple))): if isinstance(value, str): value = value.split(',') else: value = [] elif (name in _LISTFIELDS and - not isinstance(value, (list, tuple))): + not isinstance(value, (list, tuple))): if isinstance(value, str): value = [value] else: @@ -408,7 +408,7 @@ self._set_best_version() def get(self, name): - """Gets a metadata field.""" + """Get a metadata field.""" name = self._convert_name(name) if name not in self._fields: return self._default_value(name) @@ -443,24 +443,21 @@ return value def check(self): - """Checks if the metadata are compliant.""" + """Check if the metadata is compliant.""" # XXX should check the versions (if the file was loaded) - missing = [] + missing, warnings = [], [] for attr in ('Name', 'Version', 'Home-page'): value = self[attr] if value == 'UNKNOWN': missing.append(attr) if _HAS_DOCUTILS: - warnings = self._check_rst_data(self['Description']) - else: - warnings = [] + warnings.extend(self._check_rst_data(self['Description'])) # checking metadata 1.2 (XXX needs to check 1.1, 1.0) if self['Metadata-Version'] != '1.2': return missing, warnings - def is_valid_predicates(value): for v in value: if not is_valid_predicate(v.split(';')[0]): @@ -468,16 +465,15 @@ return True for fields, controller in ((_PREDICATE_FIELDS, is_valid_predicates), - (_VERSIONS_FIELDS, is_valid_versions), - (_VERSION_FIELDS, is_valid_version)): + (_VERSIONS_FIELDS, is_valid_versions), + (_VERSION_FIELDS, is_valid_version)): for field in fields: value = self[field] if value == 'UNKNOWN': continue if not controller(value): - warnings.append('Wrong value for "%s": %s' \ - % (field, value)) + warnings.append('Wrong value for %r: %s' % (field, value)) return missing, warnings @@ -494,7 +490,6 @@ # # micro-language for PEP 345 environment markers # -_STR_LIMIT = "'\"" # allowed operators _OPERATORS = {'==': lambda x, y: x == y, @@ -506,17 +501,18 @@ 'in': lambda x, y: x in y, 'not in': lambda x, y: x not in y} + def _operate(operation, x, y): return _OPERATORS[operation](x, y) # restricted set of variables _VARS = {'sys.platform': sys.platform, - 'python_version': '%s.%s' % (sys.version_info[0], - sys.version_info[1]), - 'python_full_version': sys.version.split()[0], + 'python_version': sys.version[:3], + 'python_full_version': sys.version.split(' ', 1)[0], 'os.name': os.name, - 'platform.version': platform.version, - 'platform.machine': platform.machine} + 'platform.version': platform.version(), + 'platform.machine': platform.machine()} + class _Operation(object): @@ -539,7 +535,7 @@ def _is_string(self, value): if value is None or len(value) < 2: return False - for delimiter in _STR_LIMIT: + for delimiter in '"\'': if value[0] == value[-1] == delimiter: return True return False @@ -550,7 +546,7 @@ def _convert(self, value): if value in _VARS: return self._get_var(value) - return value.strip(_STR_LIMIT) + return value.strip('"\'') def _check_name(self, value): if value not in _VARS: @@ -578,6 +574,7 @@ right = self._convert(self.right) return _operate(self.op, left, right) + class _OR(object): def __init__(self, left, right=None): self.left = left @@ -587,7 +584,7 @@ return self.right is not None def __repr__(self): - return 'OR(%s, %s)' % (repr(self.left), repr(self.right)) + return 'OR(%r, %r)' % (self.left, self.right) def __call__(self): return self.left() or self.right() @@ -602,11 +599,12 @@ return self.right is not None def __repr__(self): - return 'AND(%s, %s)' % (repr(self.left), repr(self.right)) + return 'AND(%r, %r)' % (self.left, self.right) def __call__(self): return self.left() and self.right() + class _CHAIN(object): def __init__(self, execution_context=None): @@ -668,8 +666,9 @@ return False return True + def _interpret(marker, execution_context=None): - """Interprets a marker and return a result given the environment.""" + """Interpret a marker and return a result depending on environment.""" marker = marker.strip() operations = _CHAIN(execution_context) tokenize(StringIO(marker).readline, operations.eat) diff --git a/src/distutils2/mkpkg.py b/src/distutils2/mkpkg.py --- a/src/distutils2/mkpkg.py +++ b/src/distutils2/mkpkg.py @@ -30,7 +30,11 @@ # # Detect scripts (not sure how. #! outside of package?) -import sys, os, re, shutil, ConfigParser +import sys +import os +import re +import shutil +import ConfigParser helpText = { @@ -629,19 +633,20 @@ print '\nERROR: You must select "Y" or "N".\n' -def ask(question, default = None, helptext = None, required = True, - lengthy = False, multiline = False): - prompt = '%s: ' % ( question, ) +def ask(question, default=None, helptext=None, required=True, + lengthy=False, multiline=False): + prompt = '%s: ' % (question,) if default: - prompt = '%s [%s]: ' % ( question, default ) + prompt = '%s [%s]: ' % (question, default) if default and len(question) + len(default) > 70: - prompt = '%s\n [%s]: ' % ( question, default ) + prompt = '%s\n [%s]: ' % (question, default) if lengthy or multiline: prompt += '\n >' - if not helptext: helptext = 'No additional help available.' - if helptext[0] == '\n': helptext = helptext[1:] - if helptext[-1] == '\n': helptext = helptext[:-1] + if not helptext: + helptext = 'No additional help available.' + + helptext = helptext.strip("\n") while True: sys.stdout.write(prompt) @@ -653,12 +658,14 @@ print helptext print '=' * 70 continue - if default and not line: return(default) + if default and not line: + return(default) if not line and required: print '*' * 70 print 'This value cannot be empty.' print '===========================' - if helptext: print helptext + if helptext: + print helptext print '*' * 70 continue return(line) @@ -669,7 +676,8 @@ for key in troveList: subDict = dict for subkey in key.split(' :: '): - if not subkey in subDict: subDict[subkey] = {} + if not subkey in subDict: + subDict[subkey] = {} subDict = subDict[subkey] return(dict) troveDict = buildTroveDict(troveList) @@ -687,7 +695,8 @@ def lookupOption(self, key): - if not self.config.has_option('DEFAULT', key): return(None) + if not self.config.has_option('DEFAULT', key): + return(None) return(self.config.get('DEFAULT', key)) @@ -706,7 +715,8 @@ self.config.set('DEFAULT', compareKey, self.setupData[compareKey]) - if not valuesDifferent: return + if not valuesDifferent: + return self.config.write(open(os.path.expanduser('~/.pygiver'), 'w')) @@ -718,7 +728,7 @@ def inspectFile(self, path): fp = open(path, 'r') try: - for line in [ fp.readline() for x in range(10) ]: + for line in [fp.readline() for x in range(10)]: m = re.match(r'^#!.*python((?P\d)(\.\d+)?)?$', line) if m: if m.group('major') == '3': @@ -737,14 +747,14 @@ self.setupData['name'] = m.group(1) self.setupData['version'] = m.group(2) - for root, dirs, files in os.walk('.'): + for root, dirs, files in os.walk(os.curdir): for file in files: - if root == '.' and file == 'setup.py': continue + if root == os.curdir and file == 'setup.py': continue fileName = os.path.join(root, file) self.inspectFile(fileName) if file == '__init__.py': - trySrc = os.path.join('.', 'src') + trySrc = os.path.join(os.curdir, 'src') tmpRoot = root if tmpRoot.startswith(trySrc): tmpRoot = tmpRoot[len(trySrc):] @@ -761,16 +771,16 @@ self.setupData.get('version'), helpText['version']) self.setupData['description'] = ask('Package description', self.setupData.get('description'), helpText['description'], - lengthy = True) + lengthy=True) self.setupData['author'] = ask('Author name', self.setupData.get('author'), helpText['author']) self.setupData['author_email'] = ask('Author e-mail address', self.setupData.get('author_email'), helpText['author_email']) self.setupData['url'] = ask('Project URL', - self.setupData.get('url'), helpText['url'], required = False) + self.setupData.get('url'), helpText['url'], required=False) if (askYn('Do you want to set Trove classifiers?', - helptext = helpText['do_classifier']) == 'y'): + helptext=helpText['do_classifier']) == 'y'): self.setTroveClassifier() @@ -781,8 +791,10 @@ def setTroveOther(self, classifierDict): - if askYn('Do you want to set other trove identifiers', 'n', - helpText['trove_generic']) != 'y': return + if askYn('Do you want to set other trove identifiers', + 'n', + helpText['trove_generic']) != 'y': + return self.walkTrove(classifierDict, [troveDict], '') @@ -799,7 +811,7 @@ continue if askYn('Do you want to set items under\n "%s" (%d sub-items)' - % ( key, len(trove[key]) ), 'n', + % (key, len(trove[key])), 'n', helpText['trove_generic']) == 'y': self.walkTrove(classifierDict, trovePath + [trove[key]], desc + ' :: ' + key) @@ -808,15 +820,18 @@ def setTroveLicense(self, classifierDict): while True: license = ask('What license do you use', - helptext = helpText['trove_license'], required = False) - if not license: return + helptext=helpText['trove_license'], + required=False) + if not license: + return licenseWords = license.lower().split(' ') foundList = [] for index in range(len(troveList)): troveItem = troveList[index] - if not troveItem.startswith('License :: '): continue + if not troveItem.startswith('License :: '): + continue troveItem = troveItem[11:].lower() allMatch = True @@ -824,17 +839,20 @@ if not word in troveItem: allMatch = False break - if allMatch: foundList.append(index) + if allMatch: + foundList.append(index) question = 'Matching licenses:\n\n' for i in xrange(1, len(foundList) + 1): - question += ' %s) %s\n' % ( i, troveList[foundList[i - 1]] ) + question += ' %s) %s\n' % (i, troveList[foundList[i - 1]]) question += ('\nType the number of the license you wish to use or ' '? to try again:') - troveLicense = ask(question, required = False) + troveLicense = ask(question, required=False) - if troveLicense == '?': continue - if troveLicense == '': return + if troveLicense == '?': + continue + if troveLicense == '': + return foundIndex = foundList[int(troveLicense) - 1] classifierDict[troveList[foundIndex]] = 1 try: @@ -856,7 +874,7 @@ 6 - Mature 7 - Inactive -Status''', required = False) +Status''', required=False) if devStatus: try: key = { @@ -884,7 +902,8 @@ return modified_pkgs def writeSetup(self): - if os.path.exists('setup.py'): shutil.move('setup.py', 'setup.py.old') + if os.path.exists('setup.py'): + shutil.move('setup.py', 'setup.py.old') fp = open('setup.py', 'w') try: diff --git a/src/distutils2/tests/__init__.py b/src/distutils2/tests/__init__.py --- a/src/distutils2/tests/__init__.py +++ b/src/distutils2/tests/__init__.py @@ -23,7 +23,7 @@ from test.test_support import TESTFN # use TESTFN from stdlib/test_support. -here = os.path.dirname(__file__) +here = os.path.dirname(__file__) or os.curdir verbose = 1 diff --git a/src/distutils2/tests/conversions/02_after.py b/src/distutils2/tests/conversions/02_after.py --- a/src/distutils2/tests/conversions/02_after.py +++ b/src/distutils2/tests/conversions/02_after.py @@ -1,4 +1,4 @@ -# -*- encoding: utf8 -*- +# -*- encoding: utf-8 -*- import sys import os from distutils2.core import setup, Extension diff --git a/src/distutils2/tests/conversions/02_before.py b/src/distutils2/tests/conversions/02_before.py --- a/src/distutils2/tests/conversions/02_before.py +++ b/src/distutils2/tests/conversions/02_before.py @@ -1,4 +1,4 @@ -# -*- encoding: utf8 -*- +# -*- encoding: utf-8 -*- import sys import os from distutils.core import setup, Extension diff --git a/src/distutils2/tests/pypi_server.py b/src/distutils2/tests/pypi_server.py --- a/src/distutils2/tests/pypi_server.py +++ b/src/distutils2/tests/pypi_server.py @@ -1,14 +1,14 @@ """Mock PyPI Server implementation, to use in tests. This module also provides a simple test case to extend if you need to use -the PyPIServer all along your test case. Be sure to read the documentation +the PyPIServer all along your test case. Be sure to read the documentation before any use. XXX TODO: The mock server can handle simple HTTP request (to simulate a simple index) or XMLRPC requests, over HTTP. Both does not have the same intergface to deal -with, and I think it's a pain. +with, and I think it's a pain. A good idea could be to re-think a bit the way dstributions are handled in the mock server. As it should return malformed HTML pages, we need to keep the @@ -21,12 +21,12 @@ >>> server.startXMLRPC() Then, the server must have only one port to rely on, eg. - + >>> server.fulladress() "http://ip:port/" It could be simple to have one HTTP server, relaying the requests to the two -implementations (static HTTP and XMLRPC over HTTP). +implementations (static HTTP and XMLRPC over HTTP). """ import Queue @@ -53,7 +53,7 @@ return use_pypi_server(*server_args, **server_kwargs) def use_pypi_server(*server_args, **server_kwargs): - """Decorator to make use of the PyPIServer for test methods, + """Decorator to make use of the PyPIServer for test methods, just when needed, and not for the entire duration of the testcase. """ def wrapper(func): @@ -79,18 +79,18 @@ self.pypi.stop() class PyPIServer(threading.Thread): - """PyPI Mock server. + """PyPI Mocked server. Provides a mocked version of the PyPI API's, to ease tests. Support serving static content and serving previously given text. """ def __init__(self, test_static_path=None, - static_filesystem_paths=["default"], + static_filesystem_paths=["default"], static_uri_paths=["simple"], serve_xmlrpc=False) : """Initialize the server. - - Default behavior is to start the HTTP server. You can either start the + + Default behavior is to start the HTTP server. You can either start the xmlrpc server by setting xmlrpc to True. Caution: Only one server will be started. @@ -103,9 +103,9 @@ threading.Thread.__init__(self) self._run = True self._serve_xmlrpc = serve_xmlrpc - + #TODO allow to serve XMLRPC and HTTP static files at the same time. - if not self._serve_xmlrpc: + if not self._serve_xmlrpc: self.server = HTTPServer(('', 0), PyPIRequestHandler) self.server.RequestHandlerClass.pypi_server = self @@ -114,7 +114,7 @@ self.default_response_status = 200 self.default_response_headers = [('Content-type', 'text/plain')] self.default_response_data = "hello" - + # initialize static paths / filesystems self.static_uri_paths = static_uri_paths if test_static_path is not None: @@ -201,7 +201,7 @@ # serve the content from local disc if we request an URL beginning # by a pattern defined in `static_paths` url_parts = self.path.split("/") - if (len(url_parts) > 1 and + if (len(url_parts) > 1 and url_parts[1] in self.pypi_server.static_uri_paths): data = None # always take the last first. @@ -239,7 +239,7 @@ try: status = int(status) except ValueError: - # we probably got something like YYY Codename. + # we probably got something like YYY Codename. # Just get the first 3 digits status = int(status[:3]) @@ -258,15 +258,15 @@ self.server_port = port class MockDist(object): - """Fake distribution, used in the Mock PyPI Server""" + """Fake distribution, used in the Mock PyPI Server""" def __init__(self, name, version="1.0", hidden=False, url="http://url/", type="sdist", filename="", size=10000, digest="123456", downloads=7, has_sig=False, - python_version="source", comment="comment", - author="John Doe", author_email="john at doe.name", + python_version="source", comment="comment", + author="John Doe", author_email="john at doe.name", maintainer="Main Tayner", maintainer_email="maintainer_mail", project_url="http://project_url/", homepage="http://homepage/", - keywords="", platform="UNKNOWN", classifiers=[], licence="", + keywords="", platform="UNKNOWN", classifiers=[], licence="", description="Description", summary="Summary", stable_version="", ordering="", documentation_id="", code_kwalitee_id="", installability_id="", obsoletes=[], obsoletes_dist=[], @@ -277,7 +277,7 @@ self.name = name self.version = version self.hidden = hidden - + # URL infos self.url = url self.digest = digest @@ -286,7 +286,7 @@ self.python_version = python_version self.comment = comment self.type = type - + # metadata self.author = author self.author_email = author_email @@ -305,7 +305,7 @@ self.cheesecake_documentation_id = documentation_id self.cheesecake_code_kwalitee_id = code_kwalitee_id self.cheesecake_installability_id = installability_id - + self.obsoletes = obsoletes self.obsoletes_dist = obsoletes_dist self.provides = provides @@ -314,7 +314,7 @@ self.requires_dist = requires_dist self.requires_external = requires_external self.requires_python = requires_python - + def url_infos(self): return { 'url': self.url, @@ -330,38 +330,38 @@ def metadata(self): return { - 'maintainer': self.maintainer, - 'project_url': [self.project_url], - 'maintainer_email': self.maintainer_email, - 'cheesecake_code_kwalitee_id': self.cheesecake_code_kwalitee_id, - 'keywords': self.keywords, - 'obsoletes_dist': self.obsoletes_dist, - 'requires_external': self.requires_external, - 'author': self.author, + 'maintainer': self.maintainer, + 'project_url': [self.project_url], + 'maintainer_email': self.maintainer_email, + 'cheesecake_code_kwalitee_id': self.cheesecake_code_kwalitee_id, + 'keywords': self.keywords, + 'obsoletes_dist': self.obsoletes_dist, + 'requires_external': self.requires_external, + 'author': self.author, 'author_email': self.author_email, - 'download_url': self.url, - 'platform': self.platform, - 'version': self.version, - 'obsoletes': self.obsoletes, - 'provides': self.provides, - 'cheesecake_documentation_id': self.cheesecake_documentation_id, - '_pypi_hidden': self.hidden, - 'description': self.description, - '_pypi_ordering': 19, - 'requires_dist': self.requires_dist, - 'requires_python': self.requires_python, - 'classifiers': [], - 'name': self.name, - 'licence': self.licence, - 'summary': self.summary, - 'home_page': self.homepage, - 'stable_version': self.stable_version, + 'download_url': self.url, + 'platform': self.platform, + 'version': self.version, + 'obsoletes': self.obsoletes, + 'provides': self.provides, + 'cheesecake_documentation_id': self.cheesecake_documentation_id, + '_pypi_hidden': self.hidden, + 'description': self.description, + '_pypi_ordering': 19, + 'requires_dist': self.requires_dist, + 'requires_python': self.requires_python, + 'classifiers': [], + 'name': self.name, + 'licence': self.licence, + 'summary': self.summary, + 'home_page': self.homepage, + 'stable_version': self.stable_version, 'provides_dist': self.provides_dist or "%s (%s)" % (self.name, self.version), - 'requires': self.requires, + 'requires': self.requires, 'cheesecake_installability_id': self.cheesecake_installability_id, } - + def search_result(self): return { '_pypi_ordering': 0, @@ -372,7 +372,7 @@ class XMLRPCMockIndex(object): """Mock XMLRPC server""" - + def __init__(self, dists=[]): self._dists = dists @@ -386,7 +386,7 @@ def set_search_result(self, result): """set a predefined search result""" - self._search_result = result + self._search_result = result def _get_search_results(self): results = [] @@ -401,7 +401,7 @@ return [r.search_result() for r in results] def list_package(self): - return [d.name for d in self._dists] + return [d.name for d in self._dists] def package_releases(self, package_name, show_hidden=False): if show_hidden: @@ -411,14 +411,14 @@ # return only un-hidden return [d.version for d in self._dists if d.name == package_name and not d.hidden] - + def release_urls(self, package_name, version): - return [d.url_infos() for d in self._dists + return [d.url_infos() for d in self._dists if d.name == package_name and d.version == version] def release_data(self, package_name, version): - release = [d for d in self._dists - if d.name == package_name and d.version == version] + release = [d for d in self._dists + if d.name == package_name and d.version == version] if release: return release[0].metadata() else: diff --git a/src/distutils2/tests/support.py b/src/distutils2/tests/support.py --- a/src/distutils2/tests/support.py +++ b/src/distutils2/tests/support.py @@ -3,6 +3,27 @@ Always import unittest from this module, it will be the right version (standard library unittest for 2.7 and higher, third-party unittest2 release for older versions). + +Three helper classes are provided: LoggingSilencer, TempdirManager and +EnvironGuard. They are written to be used as mixins, e.g. :: + + from distutils2.tests.support import unittest + from distutils2.tests.support import LoggingSilencer + + class SomeTestCase(LoggingSilencer, unittest.TestCase): + +If you need to define a setUp method on your test class, you have to +call the mixin class' setUp method or it won't work (same thing for +tearDown): + + def setUp(self): + super(self.__class__, self).setUp() + ... # other setup code + +Read each class' docstring to see their purpose and usage. + +Also provided is a DummyCommand class, useful to mock commands in the +tests of another command that needs them (see docstring). """ import os @@ -10,26 +31,36 @@ import shutil import tempfile from copy import deepcopy -import warnings from distutils2 import log from distutils2.log import DEBUG, INFO, WARN, ERROR, FATAL -if sys.version_info >= (2, 7): - # improved unittest package from 2.7's standard library +if sys.version_info >= (3, 2): + # improved unittest package from 3.2's standard library import unittest else: # external release of same package for older versions import unittest2 as unittest +__all__ = ['LoggingSilencer', 'TempdirManager', 'EnvironGuard', + 'DummyCommand', 'unittest'] + + class LoggingSilencer(object): + """TestCase-compatible mixin to catch logging calls. + + Every log message that goes through distutils2.log will get appended to + self.logs instead of being printed. You can check that your code logs + warnings and errors as documented by inspecting that list; helper methods + get_logs and clear_logs are also provided. + """ def setUp(self): super(LoggingSilencer, self).setUp() - self.threshold = log.set_threshold(log.FATAL) + self.threshold = log.set_threshold(FATAL) # catching warnings - # when log will be replaced by logging - # we won't need such monkey-patch anymore + # when log is replaced by logging we won't need + # such monkey-patching anymore self._old_log = log.Log._log log.Log._log = self._log self.logs = [] @@ -45,6 +76,10 @@ self.logs.append((level, msg, args)) def get_logs(self, *levels): + """Return a list of caught messages with level in `levels`. + + Example: self.get_logs(log.WARN, log.DEBUG) -> list + """ def _format(msg, args): if len(args) == 0: return msg @@ -53,48 +88,41 @@ in self.logs if level in levels] def clear_logs(self): - self.logs = [] + """Empty the internal list of caught messages.""" + del self.logs[:] + class TempdirManager(object): - """Mix-in class that handles temporary directories for test cases. + """TestCase-compatible mixin to create temporary directories and files. - This is intended to be used with unittest.TestCase. + Directories and files created in a test_* method will be removed after it + has run. """ def setUp(self): super(TempdirManager, self).setUp() - self.tempdirs = [] - self.tempfiles = [] + self._basetempdir = tempfile.mkdtemp() def tearDown(self): super(TempdirManager, self).tearDown() - while self.tempdirs: - d = self.tempdirs.pop() - shutil.rmtree(d, os.name in ('nt', 'cygwin')) - for file_ in self.tempfiles: - if os.path.exists(file_): - os.remove(file_) + shutil.rmtree(self._basetempdir, os.name in ('nt', 'cygwin')) def mktempfile(self): - """Create a temporary file that will be cleaned up.""" - tempfile_ = tempfile.NamedTemporaryFile() - self.tempfiles.append(tempfile_.name) - return tempfile_ + """Create a read-write temporary file and return it.""" + fd, fn = tempfile.mkstemp(dir=self._basetempdir) + os.close(fd) + return open(fn, 'w+') def mkdtemp(self): - """Create a temporary directory that will be cleaned up. - - Returns the path of the directory. - """ - d = tempfile.mkdtemp() - self.tempdirs.append(d) + """Create a temporary directory and return its path.""" + d = tempfile.mkdtemp(dir=self._basetempdir) return d def write_file(self, path, content='xxx'): - """Writes a file in the given path. + """Write a file at the given path. - - path can be a string or a sequence. + path can be a string, a tuple or a list; if it's a tuple or list, + os.path.join will be used to produce a path. """ if isinstance(path, (list, tuple)): path = os.path.join(*path) @@ -105,41 +133,35 @@ f.close() def create_dist(self, pkg_name='foo', **kw): - """Will generate a test environment. + """Create a stub distribution object and files. - This function creates: - - a Distribution instance using keywords - - a temporary directory with a package structure + This function creates a Distribution instance (use keyword arguments + to customize it) and a temporary directory with a project structure + (currently an empty directory). - It returns the package directory and the distribution - instance. + It returns the path to the directory and the Distribution instance. + You can use TempdirManager.write_file to write any file in that + directory, e.g. setup scripts or Python modules. """ + # Late import so that third parties can import support without + # loading a ton of distutils2 modules in memory. from distutils2.dist import Distribution tmp_dir = self.mkdtemp() pkg_dir = os.path.join(tmp_dir, pkg_name) os.mkdir(pkg_dir) dist = Distribution(attrs=kw) - return pkg_dir, dist -class DummyCommand: - """Class to store options for retrieval via set_undefined_options().""" - - def __init__(self, **kwargs): - for kw, val in kwargs.items(): - setattr(self, kw, val) - - def ensure_finalized(self): - pass class EnvironGuard(object): + """TestCase-compatible mixin to save and restore the environment.""" def setUp(self): super(EnvironGuard, self).setUp() self.old_environ = deepcopy(os.environ) def tearDown(self): - for key, value in self.old_environ.items(): + for key, value in self.old_environ.iteritems(): if os.environ.get(key) != value: os.environ[key] = value @@ -148,3 +170,18 @@ del os.environ[key] super(EnvironGuard, self).tearDown() + + +class DummyCommand(object): + """Class to store options for retrieval via set_undefined_options(). + + Useful for mocking one dependency command in the tests for another + command, see e.g. the dummy build command in test_build_scripts. + """ + + def __init__(self, **kwargs): + for kw, val in kwargs.iteritems(): + setattr(self, kw, val) + + def ensure_finalized(self): + pass diff --git a/src/distutils2/tests/test_Mixin2to3.py b/src/distutils2/tests/test_Mixin2to3.py --- a/src/distutils2/tests/test_Mixin2to3.py +++ b/src/distutils2/tests/test_Mixin2to3.py @@ -1,6 +1,5 @@ """Tests for distutils.command.build_py.""" import sys -import tempfile import distutils2 from distutils2.tests import support @@ -10,7 +9,7 @@ class Mixin2to3TestCase(support.TempdirManager, unittest.TestCase): - @unittest.skipUnless(sys.version > '2.6', 'Need >= 2.6') + @unittest.skipIf(sys.version < '2.6', 'requires Python 2.6 or higher') def test_convert_code_only(self): # used to check if code gets converted properly. code_content = "print 'test'\n" @@ -27,7 +26,7 @@ self.assertEquals(new_code_content, converted_code_content) - @unittest.skipUnless(sys.version > '2.6', 'Need >= 2.6') + @unittest.skipIf(sys.version < '2.6', 'requires Python 2.6 or higher') def test_doctests_only(self): # used to check if doctests gets converted properly. doctest_content = '"""\n>>> print test\ntest\n"""\nprint test\n\n' diff --git a/src/distutils2/tests/test_bdist_msi.py b/src/distutils2/tests/test_bdist_msi.py --- a/src/distutils2/tests/test_bdist_msi.py +++ b/src/distutils2/tests/test_bdist_msi.py @@ -10,8 +10,8 @@ support.LoggingSilencer, unittest.TestCase): - @unittest.skipUnless(sys.platform=="win32", "These tests are only for win32") - def test_minial(self): + @unittest.skipUnless(sys.platform == "win32", "runs only on win32") + def test_minimal(self): # minimal test XXX need more tests from distutils2.command.bdist_msi import bdist_msi pkg_pth, dist = self.create_dist() diff --git a/src/distutils2/tests/test_build_ext.py b/src/distutils2/tests/test_build_ext.py --- a/src/distutils2/tests/test_build_ext.py +++ b/src/distutils2/tests/test_build_ext.py @@ -45,7 +45,7 @@ build_ext.USER_BASE = site.USER_BASE # XXX only works with 2.6 > -- dunno why yet - @unittest.skipUnless(sys.version_info >= (2, 6,), 'works for >= 2.6') + @unittest.skipIf(sys.version < '2.6', 'requires Python 2.6 or higher') def test_build_ext(self): global ALREADY_TESTED xx_c = os.path.join(self.tmp_dir, 'xxmodule.c') @@ -126,11 +126,8 @@ # make sure we get some library dirs under solaris self.assertTrue(len(cmd.library_dirs) > 0) + @unittest.skipIf(sys.version < '2.6', 'requires Python 2.6 or higher') def test_user_site(self): - # site.USER_SITE was introduced in 2.6 - if sys.version < '2.6': - return - import site dist = Distribution({'name': 'xx'}) cmd = build_ext(dist) diff --git a/src/distutils2/tests/test_build_py.py b/src/distutils2/tests/test_build_py.py --- a/src/distutils2/tests/test_build_py.py +++ b/src/distutils2/tests/test_build_py.py @@ -95,7 +95,7 @@ sys.stdout = old_stdout @unittest.skipUnless(hasattr(sys, 'dont_write_bytecode'), - 'dont_write_bytecode support') + 'sys.dont_write_bytecode not supported') def test_dont_write_bytecode(self): # makes sure byte_compile is not used pkg_dir, dist = self.create_dist() diff --git a/src/distutils2/tests/test_check.py b/src/distutils2/tests/test_check.py --- a/src/distutils2/tests/test_check.py +++ b/src/distutils2/tests/test_check.py @@ -43,7 +43,7 @@ # get an error if there are missing metadata self.assertRaises(DistutilsSetupError, self._run, {}, **{'strict': 1}) - # and of course, no error when all metadata are present + # and of course, no error when all metadata fields are present cmd = self._run(metadata, strict=1) self.assertEqual(len(cmd._warnings), 0) diff --git a/src/distutils2/tests/test_cmd.py b/src/distutils2/tests/test_cmd.py --- a/src/distutils2/tests/test_cmd.py +++ b/src/distutils2/tests/test_cmd.py @@ -98,7 +98,7 @@ def test_ensure_dirname(self): cmd = self.cmd - cmd.option1 = os.path.dirname(__file__) + cmd.option1 = os.path.dirname(__file__) or os.curdir cmd.ensure_dirname('option1') cmd.option2 = 'xxx' self.assertRaises(DistutilsOptionError, cmd.ensure_dirname, 'option2') diff --git a/src/distutils2/tests/test_converter.py b/src/distutils2/tests/test_converter.py --- a/src/distutils2/tests/test_converter.py +++ b/src/distutils2/tests/test_converter.py @@ -17,7 +17,7 @@ class ConverterTestCase(unittest.TestCase): - @unittest.skipUnless(not sys.version < '2.6', 'Needs Python >=2.6') + @unittest.skipIf(sys.version < '2.6', 'requires Python 2.6 or higher') def test_conversions(self): # for all XX_before in the conversions/ dir # we run the refactoring tool diff --git a/src/distutils2/tests/test_core.py b/src/distutils2/tests/test_core.py --- a/src/distutils2/tests/test_core.py +++ b/src/distutils2/tests/test_core.py @@ -64,13 +64,13 @@ f = self.write_setup(setup_using___file__) for s in ['init', 'config', 'commandline', 'run']: distutils2.core.run_setup(f, stop_after=s) - self.assertRaises(ValueError, distutils2.core.run_setup, + self.assertRaises(ValueError, distutils2.core.run_setup, f, stop_after='bob') def test_run_setup_args(self): f = self.write_setup(setup_using___file__) - d = distutils2.core.run_setup(f, script_args=["--help"], - stop_after="init") + d = distutils2.core.run_setup(f, script_args=["--help"], + stop_after="init") self.assertEqual(['--help'], d.script_args) def test_run_setup_uses_current_dir(self): diff --git a/src/distutils2/tests/test_dist.py b/src/distutils2/tests/test_dist.py --- a/src/distutils2/tests/test_dist.py +++ b/src/distutils2/tests/test_dist.py @@ -1,4 +1,4 @@ -# -*- coding: utf8 -*- +# -*- coding: utf-8 -*- """Tests for distutils2.dist.""" import os @@ -153,6 +153,27 @@ my_file2 = os.path.join(tmp_dir, 'f2') dist.metadata.write_file(open(my_file, 'w')) + def test_bad_attr(self): + cls = Distribution + + # catching warnings + warns = [] + def _warn(msg): + warns.append(msg) + + old_warn = warnings.warn + warnings.warn = _warn + try: + dist = cls(attrs={'author': 'xxx', + 'name': 'xxx', + 'version': 'xxx', + 'url': 'xxxx', + 'badoptname': 'xxx'}) + finally: + warnings.warn = old_warn + + self.assertTrue(len(warns)==1 and "Unknown distribution" in warns[0]) + def test_empty_options(self): # an empty options dictionary should not stay in the # list of attributes @@ -176,6 +197,21 @@ self.assertEqual(len(warns), 0) + def test_non_empty_options(self): + # TODO: how to actually use options is not documented except + # for a few cryptic comments in dist.py. If this is to stay + # in the public API, it deserves some better documentation. + + # Here is an example of how it's used out there: + # http://svn.pythonmac.org/py2app/py2app/trunk/doc/index.html#specifying-customizations + cls = Distribution + dist = cls(attrs={'author': 'xxx', + 'name': 'xxx', + 'version': 'xxx', + 'url': 'xxxx', + 'options': dict(sdist=dict(owner="root"))}) + self.assertTrue("owner" in dist.get_option_dict("sdist")) + def test_finalize_options(self): attrs = {'keywords': 'one,two', @@ -240,6 +276,49 @@ # make sure --no-user-cfg disables the user cfg file self.assertEqual(len(all_files)-1, len(files)) + def test_special_hooks_parsing(self): + temp_home = self.mkdtemp() + config_files = [os.path.join(temp_home, "config1.cfg"), + os.path.join(temp_home, "config2.cfg")] + + # Store two aliased hooks in config files + self.write_file((temp_home, "config1.cfg"), '[test_dist]\npre-hook.a = type') + self.write_file((temp_home, "config2.cfg"), '[test_dist]\npre-hook.b = type') + + sys.argv.extend(["--command-packages", + "distutils2.tests", + "test_dist"]) + cmd = self.create_distribution(config_files).get_command_obj("test_dist") + self.assertEqual(cmd.pre_hook, {"a": 'type', "b": 'type'}) + + + def test_hooks_get_run(self): + temp_home = self.mkdtemp() + config_file = os.path.join(temp_home, "config1.cfg") + + self.write_file((temp_home, "config1.cfg"), textwrap.dedent(''' + [test_dist] + pre-hook.test = distutils2.tests.test_dist.DistributionTestCase.log_pre_call + post-hook.test = distutils2.tests.test_dist.DistributionTestCase.log_post_call''')) + + sys.argv.extend(["--command-packages", + "distutils2.tests", + "test_dist"]) + d = self.create_distribution([config_file]) + cmd = d.get_command_obj("test_dist") + + # prepare the call recorders + record = [] + DistributionTestCase.log_pre_call = staticmethod(lambda _cmd: record.append(('pre', _cmd))) + DistributionTestCase.log_post_call = staticmethod(lambda _cmd: record.append(('post', _cmd))) + test_dist.run = lambda _cmd: record.append(('run', _cmd)) + test_dist.finalize_options = lambda _cmd: record.append(('finalize_options', _cmd)) + + d.run_command('test_dist') + self.assertEqual(record, [('finalize_options', cmd), + ('pre', cmd), + ('run', cmd), + ('post', cmd)]) class MetadataTestCase(support.TempdirManager, support.EnvironGuard, unittest.TestCase): diff --git a/src/distutils2/tests/test_index_dist.py b/src/distutils2/tests/test_index_dist.py --- a/src/distutils2/tests/test_index_dist.py +++ b/src/distutils2/tests/test_index_dist.py @@ -18,7 +18,7 @@ class TestReleaseInfo(unittest.TestCase): - def test_instanciation(self): + def test_instantiation(self): # Test the DistInfo class provides us the good attributes when # given on construction release = ReleaseInfo("FooBar", "1.1") @@ -106,7 +106,7 @@ }) self.assertEqual(2, len(d.urls)) - def test_comparaison(self): + def test_comparison(self): # Test that we can compare DistInfoributionInfoList foo1 = ReleaseInfo("foo", "1.0") foo2 = ReleaseInfo("foo", "2.0") @@ -123,23 +123,20 @@ def test_download(self, server): # Download is possible, and the md5 is checked if given - add_to_tmpdirs = lambda x: self.tempdirs.append(os.path.dirname(x)) - url = "%s/simple/foobar/foobar-0.1.tar.gz" % server.full_address # check md5 if given dist = Dist(url=url, hashname="md5", hashval="d41d8cd98f00b204e9800998ecf8427e") - add_to_tmpdirs(dist.download()) + dist.download(self.mkdtemp()) # a wrong md5 fails dist2 = Dist(url=url, hashname="md5", hashval="wrongmd5") - self.assertRaises(HashDoesNotMatch, dist2.download) - add_to_tmpdirs(dist2.downloaded_location) + self.assertRaises(HashDoesNotMatch, dist2.download, self.mkdtemp()) # we can omit the md5 hash dist3 = Dist(url=url) - add_to_tmpdirs(dist3.download()) + dist3.download(self.mkdtemp()) # and specify a temporary location # for an already downloaded dist @@ -177,7 +174,7 @@ self.assertIn(releases[0], filtered) self.assertIn(releases[1], filtered) - def test_add_release(self): + def test_append(self): # When adding a new item to the list, the behavior is to test if # a release with the same name and version number already exists, # and if so, to add a new distribution for it. If the distribution type diff --git a/src/distutils2/tests/test_index_simple.py b/src/distutils2/tests/test_index_simple.py --- a/src/distutils2/tests/test_index_simple.py +++ b/src/distutils2/tests/test_index_simple.py @@ -222,7 +222,7 @@ # create the index using both servers crawler = Crawler(server.full_address + "/simple/", hosts=('*',), timeout=1, # set the timeout to 1s for the tests - mirrors=[mirror.full_address, ]) + mirrors=[mirror.full_address]) # this should not raise a timeout self.assertEqual(4, len(crawler.get_releases("foo"))) diff --git a/src/distutils2/tests/test_install.py b/src/distutils2/tests/test_install.py --- a/src/distutils2/tests/test_install.py +++ b/src/distutils2/tests/test_install.py @@ -75,11 +75,9 @@ check_path(cmd.install_scripts, os.path.join(destination, "bin")) check_path(cmd.install_data, destination) + @unittest.skipIf(sys.version < '2.6', 'requires Python 2.6 or higher') def test_user_site(self): - # site.USER_SITE was introduced in 2.6 - if sys.version < '2.6': - return - + # test install with --user # preparing the environment for the test self.old_user_base = get_config_var('userbase') self.old_user_site = get_path('purelib', '%s_user' % os.name) @@ -195,11 +193,12 @@ cmd.ensure_finalized() cmd.run() - # let's check the RECORD file was created with one - # line (the egg info file) + # let's check the RECORD file was created with four + # lines, one for each .dist-info entry: METADATA, + # INSTALLER, REQUSTED, RECORD f = open(cmd.record) try: - self.assertEqual(len(f.readlines()), 1) + self.assertEqual(len(f.readlines()), 4) finally: f.close() diff --git a/src/distutils2/tests/test_install_distinfo.py b/src/distutils2/tests/test_install_distinfo.py new file mode 100644 --- /dev/null +++ b/src/distutils2/tests/test_install_distinfo.py @@ -0,0 +1,202 @@ +"""Tests for ``distutils2.command.install_distinfo``. """ + +import os +import sys +import csv + +from distutils2.command.install_distinfo import install_distinfo +from distutils2.core import Command +from distutils2.metadata import DistributionMetadata +from distutils2.tests import support +from distutils2.tests.support import unittest + +try: + import hashlib +except ImportError: + from distutils2._backport import hashlib + + +class DummyInstallCmd(Command): + + def __init__(self, dist=None): + self.outputs = [] + self.distribution = dist + + def __getattr__(self, name): + return None + + def ensure_finalized(self): + pass + + def get_outputs(self): + return self.outputs + \ + self.get_finalized_command('install_distinfo').get_outputs() + + +class InstallDistinfoTestCase(support.TempdirManager, + support.LoggingSilencer, + support.EnvironGuard, + unittest.TestCase): + + checkLists = lambda self, x, y: self.assertListEqual(sorted(x), sorted(y)) + + def test_empty_install(self): + pkg_dir, dist = self.create_dist(name='foo', + version='1.0') + install_dir = self.mkdtemp() + + install = DummyInstallCmd(dist) + dist.command_obj['install'] = install + + cmd = install_distinfo(dist) + dist.command_obj['install_distinfo'] = cmd + + cmd.initialize_options() + cmd.distinfo_dir = install_dir + cmd.ensure_finalized() + cmd.run() + + self.checkLists(os.listdir(install_dir), ['foo-1.0.dist-info']) + + dist_info = os.path.join(install_dir, 'foo-1.0.dist-info') + self.checkLists(os.listdir(dist_info), + ['METADATA', 'RECORD', 'REQUESTED', 'INSTALLER']) + self.assertEqual(open(os.path.join(dist_info, 'INSTALLER')).read(), + 'distutils') + self.assertEqual(open(os.path.join(dist_info, 'REQUESTED')).read(), + '') + meta_path = os.path.join(dist_info, 'METADATA') + self.assertTrue(DistributionMetadata(path=meta_path).check()) + + def test_installer(self): + pkg_dir, dist = self.create_dist(name='foo', + version='1.0') + install_dir = self.mkdtemp() + + install = DummyInstallCmd(dist) + dist.command_obj['install'] = install + + cmd = install_distinfo(dist) + dist.command_obj['install_distinfo'] = cmd + + cmd.initialize_options() + cmd.distinfo_dir = install_dir + cmd.installer = 'bacon-python' + cmd.ensure_finalized() + cmd.run() + + dist_info = os.path.join(install_dir, 'foo-1.0.dist-info') + self.assertEqual(open(os.path.join(dist_info, 'INSTALLER')).read(), + 'bacon-python') + + def test_requested(self): + pkg_dir, dist = self.create_dist(name='foo', + version='1.0') + install_dir = self.mkdtemp() + + install = DummyInstallCmd(dist) + dist.command_obj['install'] = install + + cmd = install_distinfo(dist) + dist.command_obj['install_distinfo'] = cmd + + cmd.initialize_options() + cmd.distinfo_dir = install_dir + cmd.requested = False + cmd.ensure_finalized() + cmd.run() + + dist_info = os.path.join(install_dir, 'foo-1.0.dist-info') + self.checkLists(os.listdir(dist_info), + ['METADATA', 'RECORD', 'INSTALLER']) + + def test_no_record(self): + pkg_dir, dist = self.create_dist(name='foo', + version='1.0') + install_dir = self.mkdtemp() + + install = DummyInstallCmd(dist) + dist.command_obj['install'] = install + + cmd = install_distinfo(dist) + dist.command_obj['install_distinfo'] = cmd + + cmd.initialize_options() + cmd.distinfo_dir = install_dir + cmd.no_record = True + cmd.ensure_finalized() + cmd.run() + + dist_info = os.path.join(install_dir, 'foo-1.0.dist-info') + self.checkLists(os.listdir(dist_info), + ['METADATA', 'REQUESTED', 'INSTALLER']) + + def test_record(self): + pkg_dir, dist = self.create_dist(name='foo', + version='1.0') + install_dir = self.mkdtemp() + + install = DummyInstallCmd(dist) + dist.command_obj['install'] = install + + fake_dists = os.path.join(os.path.dirname(__file__), '..', + '_backport', 'tests', 'fake_dists') + fake_dists = os.path.realpath(fake_dists) + + # for testing, we simply add all files from _backport's fake_dists + dirs = [] + for dir in os.listdir(fake_dists): + full_path = os.path.join(fake_dists, dir) + if (not dir.endswith('.egg') or dir.endswith('.egg-info') or + dir.endswith('.dist-info')) and os.path.isdir(full_path): + dirs.append(full_path) + + for dir in dirs: + for (path, subdirs, files) in os.walk(dir): + install.outputs += [os.path.join(path, f) for f in files] + install.outputs += [os.path.join('path', f + 'c') + for f in files if f.endswith('.py')] + + + cmd = install_distinfo(dist) + dist.command_obj['install_distinfo'] = cmd + + cmd.initialize_options() + cmd.distinfo_dir = install_dir + cmd.ensure_finalized() + cmd.run() + + dist_info = os.path.join(install_dir, 'foo-1.0.dist-info') + + expected = [] + for f in install.get_outputs(): + if f.endswith('.pyc') or \ + f == os.path.join(install_dir, 'foo-1.0.dist-info', 'RECORD'): + expected.append([f, '', '']) + else: + size = os.path.getsize(f) + md5 = hashlib.md5() + md5.update(open(f).read()) + hash = md5.hexdigest() + expected.append([f, hash, str(size)]) + + parsed = [] + f = open(os.path.join(dist_info, 'RECORD'), 'rb') + try: + reader = csv.reader(f, delimiter=',', + lineterminator=os.linesep, + quotechar='"') + parsed = list(reader) + finally: + f.close() + + self.maxDiff = None + self.checkLists(parsed, expected) + + +def test_suite(): + return unittest.makeSuite(InstallDistinfoTestCase) + + +if __name__ == "__main__": + unittest.main(defaultTest="test_suite") diff --git a/src/distutils2/tests/test_install_lib.py b/src/distutils2/tests/test_install_lib.py --- a/src/distutils2/tests/test_install_lib.py +++ b/src/distutils2/tests/test_install_lib.py @@ -57,9 +57,8 @@ # setting up a dist environment cmd.compile = cmd.optimize = 1 cmd.install_dir = pkg_dir - f = os.path.join(pkg_dir, 'foo.py') - self.write_file(f, '# python file') - cmd.distribution.py_modules = [pkg_dir] + f = os.path.join(pkg_dir, '__init__.py') + self.write_file(f, '# python package') cmd.distribution.ext_modules = [Extension('foo', ['xxx'])] cmd.distribution.packages = [pkg_dir] cmd.distribution.script_name = 'setup.py' @@ -74,9 +73,8 @@ # setting up a dist environment cmd.compile = cmd.optimize = 1 cmd.install_dir = pkg_dir - f = os.path.join(pkg_dir, 'foo.py') - self.write_file(f, '# python file') - cmd.distribution.py_modules = [pkg_dir] + f = os.path.join(pkg_dir, '__init__.py') + self.write_file(f, '# python package') cmd.distribution.ext_modules = [Extension('foo', ['xxx'])] cmd.distribution.packages = [pkg_dir] cmd.distribution.script_name = 'setup.py' @@ -84,7 +82,8 @@ # get_input should return 2 elements self.assertEqual(len(cmd.get_inputs()), 2) - @unittest.skipUnless(bytecode_support, 'sys.dont_write_bytecode not supported') + @unittest.skipUnless(bytecode_support, + 'sys.dont_write_bytecode not supported') def test_dont_write_bytecode(self): # makes sure byte_compile is not used pkg_dir, dist = self.create_dist() diff --git a/src/distutils2/tests/test_metadata.py b/src/distutils2/tests/test_metadata.py --- a/src/distutils2/tests/test_metadata.py +++ b/src/distutils2/tests/test_metadata.py @@ -1,6 +1,7 @@ """Tests for distutils.command.bdist.""" import os import sys +import platform from StringIO import StringIO from distutils2.metadata import (DistributionMetadata, _interpret, @@ -12,25 +13,59 @@ class DistributionMetadataTestCase(LoggingSilencer, unittest.TestCase): + def test_instantiation(self): + PKG_INFO = os.path.join(os.path.dirname(__file__), 'PKG-INFO') + fp = open(PKG_INFO) + try: + contents = fp.read() + finally: + fp.close() + fp = StringIO(contents) + + m = DistributionMetadata() + self.assertRaises(MetadataUnrecognizedVersionError, m.items) + + m = DistributionMetadata(PKG_INFO) + self.assertEqual(len(m.items()), 22) + + m = DistributionMetadata(fileobj=fp) + self.assertEqual(len(m.items()), 22) + + m = DistributionMetadata(mapping=dict(name='Test', version='1.0')) + self.assertEqual(len(m.items()), 11) + + d = dict(m.items()) + self.assertRaises(TypeError, DistributionMetadata, + PKG_INFO, fileobj=fp) + self.assertRaises(TypeError, DistributionMetadata, + PKG_INFO, mapping=d) + self.assertRaises(TypeError, DistributionMetadata, + fileobj=fp, mapping=d) + self.assertRaises(TypeError, DistributionMetadata, + PKG_INFO, mapping=m, fileobj=fp) def test_interpret(self): - platform = sys.platform + sys_platform = sys.platform version = sys.version.split()[0] os_name = os.name + platform_version = platform.version() + platform_machine = platform.machine() - self.assertTrue(_interpret("sys.platform == '%s'" % platform)) + self.assertTrue(_interpret("sys.platform == '%s'" % sys_platform)) self.assertTrue(_interpret( - "sys.platform == '%s' or python_version == '2.4'" % platform)) + "sys.platform == '%s' or python_version == '2.4'" % sys_platform)) self.assertTrue(_interpret( "sys.platform == '%s' and python_full_version == '%s'" % - (platform, version))) - self.assertTrue(_interpret("'%s' == sys.platform" % platform)) - + (sys_platform, version))) + self.assertTrue(_interpret("'%s' == sys.platform" % sys_platform)) self.assertTrue(_interpret('os.name == "%s"' % os_name)) + self.assertTrue(_interpret( + 'platform.version == "%s" and platform.machine == "%s"' % + (platform_version, platform_machine))) # stuff that need to raise a syntax error ops = ('os.name == os.name', 'os.name == 2', "'2' == '2'", - 'okpjonon', '', 'os.name ==') + 'okpjonon', '', 'os.name ==', 'python_version == 2.4') for op in ops: self.assertRaises(SyntaxError, _interpret, op) @@ -62,17 +97,13 @@ PKG_INFO = os.path.join(os.path.dirname(__file__), 'PKG-INFO') metadata = DistributionMetadata(PKG_INFO) - res = StringIO() - metadata.write_file(res) - res.seek(0) - res = res.read() - f = open(PKG_INFO) - try: - # XXX this is not used - wanted = f.read() - finally: - f.close() - self.assertTrue('Keywords: keyring,password,crypt' in res) + out = StringIO() + metadata.write_file(out) + out.seek(0) + res = DistributionMetadata() + res.read_file(out) + for k in metadata.keys(): + self.assertTrue(metadata[k] == res[k]) def test_metadata_markers(self): # see if we can be platform-aware @@ -82,6 +113,10 @@ metadata = DistributionMetadata(platform_dependent=True) metadata.read_file(StringIO(content)) self.assertEqual(metadata['Requires-Dist'], ['bar']) + metadata['Name'] = "baz; sys.platform == 'blah'" + # FIXME is None or 'UNKNOWN' correct here? + # where is that documented? + self.assertEquals(metadata['Name'], None) # test with context context = {'sys.platform': 'okook'} @@ -109,15 +144,15 @@ metadata.read_file(out) self.assertEqual(wanted, metadata['Description']) - def test_mapper_apis(self): + def test_mapping_api(self): PKG_INFO = os.path.join(os.path.dirname(__file__), 'PKG-INFO') content = open(PKG_INFO).read() content = content % sys.platform - metadata = DistributionMetadata() - metadata.read_file(StringIO(content)) + metadata = DistributionMetadata(fileobj=StringIO(content)) self.assertIn('Version', metadata.keys()) self.assertIn('0.5', metadata.values()) self.assertIn(('Version', '0.5'), metadata.items()) + #TODO test update def test_versions(self): metadata = DistributionMetadata() @@ -211,6 +246,10 @@ metadata = DistributionMetadata() metadata['Version'] = 'rr' metadata['Requires-dist'] = ['Foo (a)'] + if metadata.docutils_support: + missing, warnings = metadata.check() + self.assertEqual(len(warnings), 2) + metadata.docutils_support = False missing, warnings = metadata.check() self.assertEqual(missing, ['Name', 'Home-page']) self.assertEqual(len(warnings), 2) diff --git a/src/distutils2/tests/test_msvc9compiler.py b/src/distutils2/tests/test_msvc9compiler.py --- a/src/distutils2/tests/test_msvc9compiler.py +++ b/src/distutils2/tests/test_msvc9compiler.py @@ -64,7 +64,7 @@ class msvc9compilerTestCase(support.TempdirManager, unittest.TestCase): - @unittest.skipUnless(sys.platform=="win32", "These tests are only for win32") + @unittest.skipUnless(sys.platform == "win32", "runs only on win32") def test_no_compiler(self): # makes sure query_vcvarsall throws # a DistutilsPlatformError if the compiler @@ -86,7 +86,7 @@ finally: msvc9compiler.find_vcvarsall = old_find_vcvarsall - @unittest.skipUnless(sys.platform=="win32", "These tests are only for win32") + @unittest.skipUnless(sys.platform == "win32", "runs only on win32") def test_reg_class(self): from distutils2.msvccompiler import get_build_version if get_build_version() < 8.0: @@ -110,7 +110,7 @@ keys = Reg.read_keys(HKCU, r'Control Panel') self.assertTrue('Desktop' in keys) - @unittest.skipUnless(sys.platform=="win32", "These tests are only for win32") + @unittest.skipUnless(sys.platform == "win32", "runs only on win32") def test_remove_visual_c_ref(self): from distutils2.msvc9compiler import MSVCCompiler tempdir = self.mkdtemp() diff --git a/src/distutils2/tests/test_pypi_versions.py b/src/distutils2/tests/test_pypi_versions.py --- a/src/distutils2/tests/test_pypi_versions.py +++ b/src/distutils2/tests/test_pypi_versions.py @@ -1,43 +1,39 @@ -# -## test_pypi_versions.py -## -## A very simple test to see what percentage of the current pypi packages -## have versions that can be converted automatically by distutils' new -## suggest_normalized_version() into PEP-386 compatible versions. -## -## Requires : Python 2.5+ -## -## Written by: ssteinerX at gmail.com -# +"""PEP 386 compatibility test with current distributions on PyPI. + +A very simple test to see what percentage of the current PyPI packages +have versions that can be converted automatically by distutils2's new +suggest_normalized_version into PEP 386-compatible versions. +""" + +# XXX This file does not actually run tests, move it to a script + +# Written by ssteinerX at gmail.com + +import os +import xmlrpclib try: import cPickle as pickle -except: +except ImportError: import pickle -import xmlrpclib -import os.path - from distutils2.version import suggest_normalized_version from distutils2.tests import run_unittest from distutils2.tests.support import unittest def test_pypi(): - # - ## To re-run from scratch, just delete these two .pkl files - # + # FIXME need a better way to do that + # To re-run from scratch, just delete these two .pkl files INDEX_PICKLE_FILE = 'pypi-index.pkl' VERSION_PICKLE_FILE = 'pypi-version.pkl' package_info = version_info = [] - # - ## if there's a saved version of the package list - ## restore it - ## else: - ## pull the list down from pypi - ## save a pickled version of it - # + # if there's a saved version of the package list + # restore it + # else: + # pull the list down from pypi + # save a pickled version of it if os.path.exists(INDEX_PICKLE_FILE): print "Loading saved pypi data..." f = open(INDEX_PICKLE_FILE, 'rb') @@ -57,13 +53,11 @@ finally: f.close() - # - ## If there's a saved list of the versions from the packages - ## restore it - ## else - ## extract versions from the package list - ## save a pickled version of it - # + # If there's a saved list of the versions from the packages + # restore it + # else + # extract versions from the package list + # save a pickled version of it versions = [] if os.path.exists(VERSION_PICKLE_FILE): print "Loading saved version info..." diff --git a/src/distutils2/tests/test_register.py b/src/distutils2/tests/test_register.py --- a/src/distutils2/tests/test_register.py +++ b/src/distutils2/tests/test_register.py @@ -1,5 +1,5 @@ +# -*- encoding: utf-8 -*- """Tests for distutils.command.register.""" -# -*- encoding: utf8 -*- import sys import os import getpass @@ -161,7 +161,7 @@ # therefore used afterwards by other commands self.assertEqual(cmd.distribution.password, 'password') - def test_registering(self): + def test_registration(self): # this test runs choice 2 cmd = self._get_cmd() inputs = RawInputs('2', 'tarek', 'tarek at ziade.org') @@ -210,7 +210,7 @@ cmd.strict = 1 self.assertRaises(DistutilsSetupError, cmd.run) - # metadata are OK but long_description is broken + # metadata is OK but long_description is broken metadata = {'home_page': 'xxx', 'author': 'xxx', 'author_email': u'??x??x??', 'name': 'xxx', 'version': 'xxx', diff --git a/src/distutils2/tests/test_sdist.py b/src/distutils2/tests/test_sdist.py --- a/src/distutils2/tests/test_sdist.py +++ b/src/distutils2/tests/test_sdist.py @@ -241,7 +241,7 @@ @unittest.skipUnless(zlib, "requires zlib") def test_metadata_check_option(self): - # testing the `medata-check` option + # testing the `check-metadata` option dist, cmd = self.get_cmd(metadata={}) # this should raise some warnings ! @@ -295,7 +295,7 @@ self.assertRaises(DistutilsOptionError, cmd.finalize_options) @unittest.skipUnless(zlib, "requires zlib") - @unittest.skipUnless(UID_GID_SUPPORT, "Requires grp and pwd support") + @unittest.skipUnless(UID_GID_SUPPORT, "requires grp and pwd support") def test_make_distribution_owner_group(self): # check if tar and gzip are installed diff --git a/src/distutils2/tests/test_upload.py b/src/distutils2/tests/test_upload.py --- a/src/distutils2/tests/test_upload.py +++ b/src/distutils2/tests/test_upload.py @@ -1,5 +1,5 @@ +# -*- encoding: utf-8 -*- """Tests for distutils.command.upload.""" -# -*- encoding: utf8 -*- import os import sys @@ -101,6 +101,38 @@ self.assertEqual(handler.command, 'POST') self.assertNotIn('\n', headers['authorization']) + def test_upload_docs(self): + path = os.path.join(self.tmp_dir, 'xxx') + self.write_file(path) + command, pyversion, filename = 'xxx', '2.6', path + dist_files = [(command, pyversion, filename)] + docs_path = os.path.join(self.tmp_dir, "build", "docs") + os.makedirs(docs_path) + self.write_file(os.path.join(docs_path, "index.html"), "yellow") + self.write_file(self.rc, PYPIRC) + + # lets run it + pkg_dir, dist = self.create_dist(dist_files=dist_files, author=u'd??d??') + + cmd = upload(dist) + cmd.get_finalized_command("build").run() + cmd.upload_docs = True + cmd.ensure_finalized() + cmd.repository = self.pypi.full_address + try: + prev_dir = os.getcwd() + os.chdir(self.tmp_dir) + cmd.run() + finally: + os.chdir(prev_dir) + + handler, request_data = self.pypi.requests[-1] + action, name, content =\ + request_data.split("----------------GHSKFJDLGDS7543FJKLFHRE75642756743254")[1:4] + + self.assertIn('name=":action"', action) + self.assertIn("doc_upload", action) + def test_suite(): return unittest.makeSuite(UploadTestCase) diff --git a/src/distutils2/tests/test_upload_docs.py b/src/distutils2/tests/test_upload_docs.py --- a/src/distutils2/tests/test_upload_docs.py +++ b/src/distutils2/tests/test_upload_docs.py @@ -1,13 +1,19 @@ +# -*- encoding: utf8 -*- """Tests for distutils.command.upload_docs.""" -# -*- encoding: utf8 -*- -import httplib, os, os.path, shutil, sys, tempfile, zipfile -from cStringIO import StringIO +import os +import sys +import httplib +import shutil +import zipfile +try: + from cStringIO import StringIO +except ImportError: + from StringIO import StringIO from distutils2.command import upload_docs as upload_docs_mod from distutils2.command.upload_docs import (upload_docs, zip_dir, - encode_multipart) + encode_multipart) from distutils2.core import Distribution - from distutils2.errors import DistutilsFileError, DistutilsOptionError from distutils2.tests import support @@ -59,7 +65,7 @@ self.cmd = upload_docs(self.dist) def test_default_uploaddir(self): - sandbox = tempfile.mkdtemp() + sandbox = self.mkdtemp() previous = os.getcwd() os.chdir(sandbox) try: @@ -72,7 +78,7 @@ def prepare_sample_dir(self, sample_dir=None): if sample_dir is None: - sample_dir = tempfile.mkdtemp() + sample_dir = self.mkdtemp() os.mkdir(os.path.join(sample_dir, "docs")) self.write_file(os.path.join(sample_dir, "docs", "index.html"), "Ce mortel ennui") self.write_file(os.path.join(sample_dir, "index.html"), "Oh la la") diff --git a/src/distutils2/tests/test_util.py b/src/distutils2/tests/test_util.py --- a/src/distutils2/tests/test_util.py +++ b/src/distutils2/tests/test_util.py @@ -4,7 +4,6 @@ from copy import copy from StringIO import StringIO import subprocess -import tempfile import time from distutils2.tests import captured_stdout @@ -19,7 +18,7 @@ _find_exe_version, _MAC_OS_X_LD_VERSION, byte_compile, find_packages, spawn, find_executable, _nt_quote_args, get_pypirc_path, generate_pypirc, - read_pypirc) + read_pypirc, resolve_dotted_name) from distutils2 import util from distutils2.tests import support @@ -288,7 +287,7 @@ self.assertEqual(res[2], None) @unittest.skipUnless(hasattr(sys, 'dont_write_bytecode'), - 'no dont_write_bytecode support') + 'sys.dont_write_bytecode not supported') def test_dont_write_bytecode(self): # makes sure byte_compile raise a DistutilsError # if sys.dont_write_bytecode is True @@ -301,9 +300,9 @@ def test_newer(self): self.assertRaises(DistutilsFileError, util.newer, 'xxx', 'xxx') - self.newer_f1 = tempfile.NamedTemporaryFile() + self.newer_f1 = self.mktempfile() time.sleep(1) - self.newer_f2 = tempfile.NamedTemporaryFile() + self.newer_f2 = self.mktempfile() self.assertTrue(util.newer(self.newer_f2.name, self.newer_f1.name)) def test_find_packages(self): @@ -343,7 +342,17 @@ res = find_packages([root], ['pkg1.pkg2']) self.assertEqual(set(res), set(['pkg1', 'pkg5', 'pkg1.pkg3', 'pkg1.pkg3.pkg6'])) - @unittest.skipUnless(sys.version > '2.6', 'Need Python 2.6 or more') + def test_resolve_dotted_name(self): + self.assertEqual(UtilTestCase, resolve_dotted_name("distutils2.tests.test_util.UtilTestCase")) + self.assertEqual(UtilTestCase.test_resolve_dotted_name, + resolve_dotted_name("distutils2.tests.test_util.UtilTestCase.test_resolve_dotted_name")) + + self.assertRaises(ImportError, resolve_dotted_name, + "distutils2.tests.test_util.UtilTestCaseNot") + self.assertRaises(ImportError, resolve_dotted_name, + "distutils2.tests.test_util.UtilTestCase.nonexistent_attribute") + + @unittest.skipIf(sys.version < '2.6', 'requires Python 2.6 or higher') def test_run_2to3_on_code(self): content = "print 'test'" converted_content = "print('test')" @@ -358,7 +367,7 @@ file_handle.close() self.assertEquals(new_content, converted_content) - @unittest.skipUnless(sys.version > '2.6', 'Need Python 2.6 or more') + @unittest.skipIf(sys.version < '2.6', 'requires Python 2.6 or higher') def test_run_2to3_on_doctests(self): # to check if text files containing doctests only get converted. content = ">>> print 'test'\ntest\n" @@ -385,7 +394,7 @@ @unittest.skipUnless(os.name in ('nt', 'posix'), - 'Runs only under posix or nt') + 'runs only under posix or nt') def test_spawn(self): tmpdir = self.mkdtemp() diff --git a/src/distutils2/util.py b/src/distutils2/util.py --- a/src/distutils2/util.py +++ b/src/distutils2/util.py @@ -88,8 +88,8 @@ raise ValueError("path '%s' cannot end with '/'" % pathname) paths = pathname.split('/') - while '.' in paths: - paths.remove('.') + while os.curdir in paths: + paths.remove(os.curdir) if not paths: return os.curdir return os.path.join(*paths) @@ -121,15 +121,6 @@ path = path[1:] return os.path.join(new_root, path) - elif os.name == 'mac': - if not os.path.isabs(pathname): - return os.path.join(new_root, pathname) - else: - # Chop off volume name from start of path - elements = pathname.split(":", 1) - pathname = ":" + elements[1] - return os.path.join(new_root, pathname) - else: raise DistutilsPlatformError("nothing known about " "platform '%s'" % os.name) @@ -599,7 +590,7 @@ return path[len(root_path) + 1:].replace(os.sep, '.') -def find_packages(paths=('.',), exclude=()): +def find_packages(paths=(os.curdir,), exclude=()): """Return a list all Python packages found recursively within directories 'paths' @@ -646,6 +637,24 @@ return packages +def resolve_dotted_name(dotted_name): + module_name, rest = dotted_name.split('.')[0], dotted_name.split('.')[1:] + while len(rest) > 0: + try: + ret = __import__(module_name) + break + except ImportError: + if rest == []: + raise + module_name += ('.' + rest[0]) + rest = rest[1:] + while len(rest) > 0: + try: + ret = getattr(ret, rest.pop(0)) + except AttributeError: + raise ImportError + return ret + # utility functions for 2to3 support def run_2to3(files, doctests_only=False, fixer_names=None, options=None, diff --git a/src/runtests-cov.py b/src/runtests-cov.py --- a/src/runtests-cov.py +++ b/src/runtests-cov.py @@ -5,9 +5,20 @@ """ import sys -from os.path import dirname, islink, realpath +from os.path import dirname, islink, realpath, join, abspath from optparse import OptionParser +COVERAGE_FILE = join(dirname(abspath(__file__)), '.coverage') + +def get_coverage(): + """ Return a usable coverage object. """ + # deferred import because coverage is optional + import coverage + cov = getattr(coverage, "the_coverage", None) + if not cov: + cov = coverage.coverage(COVERAGE_FILE) + return cov + def ignore_prefixes(module): """ Return a list of prefixes to ignore in the coverage report if we want to completely skip `module`. @@ -16,16 +27,17 @@ # distributions, such a Ubuntu, really like to build link farm in # /usr/lib in order to save a few bytes on the disk. dirnames = [dirname(module.__file__)] - + pymod = module.__file__.rstrip("c") if islink(pymod): dirnames.append(dirname(realpath(pymod))) return dirnames + def parse_opts(): parser = OptionParser(usage="%prog [OPTIONS]", description="run the distutils2 unittests") - + parser.add_option("-q", "--quiet", help="do not print verbose messages", action="store_true", default=False) parser.add_option("-c", "--coverage", action="store_true", default=False, @@ -36,15 +48,23 @@ default=False, help=("Show line numbers of statements in each module " "that weren't executed.")) - + opts, args = parser.parse_args() return opts, args + def coverage_report(opts): - import coverage from distutils2.tests.support import unittest - cov = coverage.coverage() - cov.load() + cov = get_coverage() + if hasattr(cov, "load"): + # running coverage 3.x + cov.load() + morfs = None + else: + # running coverage 2.x + cov.cache = COVERAGE_FILE + cov.restore() + morfs = [m for m in cov.cexecuted.keys() if "distutils2" in m] prefixes = ["runtests", "distutils2/tests", "distutils2/_backport"] prefixes += ignore_prefixes(unittest) @@ -63,7 +83,7 @@ # that module is also completely optional pass - cov.report(omit_prefixes=prefixes, show_missing=opts.show_missing) + cov.report(morfs, omit_prefixes=prefixes, show_missing=opts.show_missing) def test_main(): @@ -71,11 +91,8 @@ verbose = not opts.quiet ret = 0 - if opts.coverage or opts.report: - import coverage - if opts.coverage: - cov = coverage.coverage() + cov = get_coverage() cov.erase() cov.start() if not opts.report: @@ -89,6 +106,7 @@ return ret + def run_tests(verbose): import distutils2.tests from distutils2.tests import run_unittest, reap_children, TestFailed @@ -108,12 +126,11 @@ finally: reap_children() + if __name__ == "__main__": try: from distutils2.tests.support import unittest except ImportError: sys.stderr.write('Error: You have to install unittest2') sys.exit(1) - sys.exit(test_main()) - diff --git a/src/runtests.py b/src/runtests.py --- a/src/runtests.py +++ b/src/runtests.py @@ -1,9 +1,12 @@ +#!/usr/bin/env python """Tests for distutils2. The tests for distutils2 are defined in the distutils2.tests package. """ + import sys + def test_main(): import distutils2.tests from distutils2.tests import run_unittest, reap_children, TestFailed @@ -23,12 +26,11 @@ finally: reap_children() + if __name__ == "__main__": try: from distutils2.tests.support import unittest except ImportError: sys.stderr.write('Error: You have to install unittest2') sys.exit(1) - sys.exit(test_main()) - diff --git a/src/setup.cfg b/src/setup.cfg new file mode 100644 --- /dev/null +++ b/src/setup.cfg @@ -0,0 +1,3 @@ +[build_ext] +# needed so that tests work without mucking with sys.path +inplace = 1 diff --git a/src/setup.py b/src/setup.py --- a/src/setup.py +++ b/src/setup.py @@ -168,29 +168,25 @@ # The _hashlib module wraps optimized implementations # of hash functions from the OpenSSL library. - exts.append(Extension('_hashlib', ['_hashopenssl.c'], + exts.append(Extension('distutils2._backport._hashlib', + ['distutils2/_backport/_hashopenssl.c'], include_dirs = [ssl_inc_dir], library_dirs = [os.path.dirname(ssl_lib)], libraries = oslibs[os.name])) else: - exts.append(Extension('_sha', ['shamodule.c']) ) - exts.append(Extension('_md5', - sources=['md5module.c', 'md5.c'], - depends=['md5.h']) ) + exts.append(Extension('distutils2._backport._sha', + ['distutils2/_backport/shamodule.c'])) + exts.append(Extension('distutils2._backport._md5', + sources=['distutils2/_backport/md5module.c', + 'distutils2/_backport/md5.c'], + depends=['distutils2/_backport/md5.h']) ) if (not ssl_lib or openssl_ver < 0x00908000): # OpenSSL doesn't do these until 0.9.8 so we'll bring our own - exts.append(Extension('_sha256', ['sha256module.c'])) - exts.append(Extension('_sha512', ['sha512module.c'])) - - def prepend_modules(filename): - return os.path.join('Modules', filename) - - # all the C code is in the Modules subdirectory, prepend the path - for ext in exts: - ext.sources = [prepend_modules(fn) for fn in ext.sources] - if hasattr(ext, 'depends') and ext.depends is not None: - ext.depends = [prepend_modules(fn) for fn in ext.depends] + exts.append(Extension('distutils2._backport._sha256', + ['distutils2/_backport/sha256module.c'])) + exts.append(Extension('distutils2._backport._sha512', + ['distutils2/_backport/sha512module.c'])) return exts diff --git a/src/tests.sh b/src/tests.sh --- a/src/tests.sh +++ b/src/tests.sh @@ -1,20 +1,18 @@ #!/bin/sh echo -n "Running tests for Python 2.4... " -rm -rf *.so -python2.4 setup.py build_ext -i -q 2> /dev/null > /dev/null +rm -f distutils2/_backport/_hashlib.so +python2.4 setup.py build_ext -f -q 2> /dev/null > /dev/null python2.4 -Wd runtests.py -q 2> /dev/null -rm -rf *.so if [ $? -ne 0 ];then echo "Failed" + rm -f distutils2/_backport/_hashlib.so exit 1 else echo "Success" fi echo -n "Running tests for Python 2.5... " -python2.5 setup.py build_ext -i -q 2> /dev/null > /dev/null python2.5 -Wd runtests.py -q 2> /dev/null -rm -rf *.so if [ $? -ne 0 ];then echo "Failed" exit 1 @@ -23,7 +21,7 @@ fi echo -n "Running tests for Python 2.6... " -python2.6 -Wd -bb -3 runtests.py -q 2> /dev/null +python2.6 -Wd runtests.py -q 2> /dev/null if [ $? -ne 0 ];then echo "Failed" exit 1 -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:48 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:48 +0200 Subject: [Python-checkins] distutils2: Merged with Tarek's repo (again) Message-ID: tarek.ziade pushed 8dc26ef8235d to distutils2: http://hg.python.org/distutils2/rev/8dc26ef8235d changeset: 519:8dc26ef8235d parent: 518:85a805a00f82 parent: 382:733c3bdaa25e user: Jeremy Kloth date: Sat Jul 17 19:34:55 2010 -0600 summary: Merged with Tarek's repo (again) files: src/distutils2/command/build_py.py, src/distutils2/command/sdist.py diff --git a/.hgignore b/.hgignore --- a/.hgignore +++ b/.hgignore @@ -6,3 +6,4 @@ MANIFEST dist/ *.swp +.coverage \ No newline at end of file diff --git a/src/CONTRIBUTORS.txt b/src/CONTRIBUTORS.txt --- a/src/CONTRIBUTORS.txt +++ b/src/CONTRIBUTORS.txt @@ -13,15 +13,18 @@ - Pior Bastida - Titus Brown - Nicolas Cadou +- Konrad Delong - Josip Djolonga - Yannick Gringas - Jeremy Kloth - Martin von L??wis - Carl Meyer -- Alexis Metaireau +- Alexis M??taireau +- Zubin Mithra - Michael Mulich -- George Peris +- George Peristerakis - Sean Reifschneider +- Luis Rojas - Erik Rose - Brian Rosner - Alexandre Vassalotti diff --git a/src/distutils2/command/bdist_dumb.py b/src/distutils2/command/bdist_dumb.py --- a/src/distutils2/command/bdist_dumb.py +++ b/src/distutils2/command/bdist_dumb.py @@ -115,8 +115,9 @@ % (repr(install.install_base), repr(install.install_platbase))) else: - archive_root = os.path.join(self.bdist_dir, - ensure_relative(install.install_base)) + archive_root = os.path.join( + self.bdist_dir, + self._ensure_relative(install.install_base)) # Make the archive filename = self.make_archive(pseudoinstall_root, @@ -135,3 +136,9 @@ else: rmtree(self.bdist_dir) + def _ensure_relative(self, path): + # copied from dir_util, deleted + drive, path = os.path.splitdrive(path) + if path[0:1] == os.sep: + path = drive + path[1:] + return path diff --git a/src/distutils2/command/bdist_wininst.py b/src/distutils2/command/bdist_wininst.py --- a/src/distutils2/command/bdist_wininst.py +++ b/src/distutils2/command/bdist_wininst.py @@ -8,6 +8,7 @@ import sys import os import string +from shutil import rmtree try: from sysconfig import get_python_version except ImportError: diff --git a/src/distutils2/command/build_py.py b/src/distutils2/command/build_py.py --- a/src/distutils2/command/build_py.py +++ b/src/distutils2/command/build_py.py @@ -9,6 +9,7 @@ import logging from glob import glob +import distutils2 from distutils2.core import Command from distutils2.errors import DistutilsOptionError, DistutilsFileError from distutils2.util import convert_path diff --git a/src/distutils2/command/cmd.py b/src/distutils2/command/cmd.py --- a/src/distutils2/command/cmd.py +++ b/src/distutils2/command/cmd.py @@ -6,7 +6,7 @@ __revision__ = "$Id: cmd.py 75192 2009-10-02 23:49:48Z tarek.ziade $" -import sys, os, re +import os, re from distutils2.errors import DistutilsOptionError from distutils2 import util from distutils2 import log @@ -447,7 +447,7 @@ # If 'outfile' must be regenerated (either because it doesn't # exist, is out-of-date, or the 'force' flag is true) then # perform the action that presumably regenerates it - if self.force or dep_util.newer_group(infiles, outfile): + if self.force or util.newer_group(infiles, outfile): self.execute(func, args, exec_msg, level) # Otherwise, print the "skip" message diff --git a/src/distutils2/command/sdist.py b/src/distutils2/command/sdist.py --- a/src/distutils2/command/sdist.py +++ b/src/distutils2/command/sdist.py @@ -18,12 +18,11 @@ from distutils2._backport.shutil import get_archive_formats from distutils2.core import Command -from distutils2 import util from distutils2.errors import (DistutilsPlatformError, DistutilsOptionError, DistutilsTemplateError) from distutils2.manifest import Manifest from distutils2 import log -from distutils2.util import convert_path, newer +from distutils2.util import convert_path def show_formats(): """Print all possible values for the 'formats' option (used by diff --git a/src/distutils2/command/upload_docs.py b/src/distutils2/command/upload_docs.py --- a/src/distutils2/command/upload_docs.py +++ b/src/distutils2/command/upload_docs.py @@ -1,4 +1,4 @@ -import base64, httplib, os.path, socket, tempfile, urlparse, zipfile +import base64, httplib, os.path, socket, urlparse, zipfile from cStringIO import StringIO from distutils2 import log from distutils2.command.upload import upload @@ -81,7 +81,6 @@ raise DistutilsFileError(mesg % upload_dir) def run(self): - tmp_dir = tempfile.mkdtemp() name = self.distribution.metadata['Name'] zip_file = zip_dir(self.upload_dir) @@ -124,7 +123,7 @@ elif r.status == 301: location = r.getheader('Location') if location is None: - location = 'http://packages.python.org/%s/' % meta.get_name() + location = 'http://packages.python.org/%s/' % name self.announce('Upload successful. Visit %s' % location, log.INFO) else: diff --git a/src/distutils2/extension.py b/src/distutils2/extension.py --- a/src/distutils2/extension.py +++ b/src/distutils2/extension.py @@ -5,14 +5,8 @@ __revision__ = "$Id: extension.py 77704 2010-01-23 09:23:15Z tarek.ziade $" -import os import warnings -try: - import sysconfig -except ImportError: - from distutils2._backport import sysconfig - # This class is really only used by the "build_ext" command, so it might # make sense to put it in distutils.command.build_ext. However, that # module is already big enough, and I want to make this class a bit more diff --git a/src/distutils2/tests/test_build_ext.py b/src/distutils2/tests/test_build_ext.py --- a/src/distutils2/tests/test_build_ext.py +++ b/src/distutils2/tests/test_build_ext.py @@ -368,4 +368,4 @@ else: return unittest.makeSuite(BuildExtTestCase) if __name__ == '__main__': - distsutils2.tests.run_unittest(test_suite()) + distutils2.tests.run_unittest(test_suite()) diff --git a/src/distutils2/tests/test_depgraph.py b/src/distutils2/tests/test_depgraph.py --- a/src/distutils2/tests/test_depgraph.py +++ b/src/distutils2/tests/test_depgraph.py @@ -10,7 +10,7 @@ import re try: import cStringIO as StringIO -except ImportErorr: +except ImportError: import StringIO class DepGraphTestCase(support.LoggingSilencer, diff --git a/src/distutils2/tests/test_manifest.py b/src/distutils2/tests/test_manifest.py --- a/src/distutils2/tests/test_manifest.py +++ b/src/distutils2/tests/test_manifest.py @@ -3,6 +3,7 @@ import sys import logging +from distutils2.tests import run_unittest from distutils2.tests import support from distutils2.tests.support import unittest from distutils2.manifest import Manifest diff --git a/src/distutils2/tests/test_metadata.py b/src/distutils2/tests/test_metadata.py --- a/src/distutils2/tests/test_metadata.py +++ b/src/distutils2/tests/test_metadata.py @@ -5,7 +5,10 @@ from distutils2.metadata import (DistributionMetadata, _interpret, PKG_INFO_PREFERRED_VERSION) +from distutils2.tests import run_unittest from distutils2.tests.support import unittest, LoggingSilencer +from distutils2.errors import (MetadataConflictError, + MetadataUnrecognizedVersionError) class DistributionMetadataTestCase(LoggingSilencer, unittest.TestCase): @@ -125,6 +128,10 @@ metadata['Obsoletes-Dist'] = 'ok' self.assertEqual(metadata['Metadata-Version'], '1.2') + self.assertRaises(MetadataConflictError, metadata.set, + 'Obsoletes', 'ok') + + del metadata['Obsoletes'] del metadata['Obsoletes-Dist'] metadata['Version'] = '1' self.assertEqual(metadata['Metadata-Version'], '1.0') @@ -139,6 +146,9 @@ metadata.read_file(StringIO(open(PKG_INFO).read())) self.assertEqual(metadata['Metadata-Version'], '1.1') + metadata.version = '1.618' + self.assertRaises(MetadataUnrecognizedVersionError, metadata.keys) + def test_warnings(self): metadata = DistributionMetadata() diff --git a/src/distutils2/tests/test_pypi_versions.py b/src/distutils2/tests/test_pypi_versions.py --- a/src/distutils2/tests/test_pypi_versions.py +++ b/src/distutils2/tests/test_pypi_versions.py @@ -19,6 +19,7 @@ import os.path from distutils2.version import suggest_normalized_version +from distutils2.tests import run_unittest from distutils2.tests.support import unittest def test_pypi(): @@ -52,7 +53,7 @@ print "Saving package info..." f = open(INDEX_PICKLE_FILE, 'wb') try: - pickle.dump(package_info, o) + pickle.dump(package_info, f) finally: f.close() diff --git a/src/distutils2/version.py b/src/distutils2/version.py --- a/src/distutils2/version.py +++ b/src/distutils2/version.py @@ -1,4 +1,3 @@ -import sys import re from distutils2.errors import IrrationalVersionError, HugeMajorVersionNumError diff --git a/src/runtests-cov.py b/src/runtests-cov.py --- a/src/runtests-cov.py +++ b/src/runtests-cov.py @@ -4,32 +4,35 @@ The tests for distutils2 are defined in the distutils2.tests package. """ -# TODO: - -# The coverage report is only accurate when ran inside a virtualenv -# created with the --no-site-packages option. When it's not the case, -# the built-in ignore list is not accurate and third party packages -# show-up in the report, lowering the overall coverage. - -# One particular problem it docutils on Ubuntu which has a __file__ -# starting with /usr/lib/python2.6 while the path in the coverage -# report starts with /usr/share/pyshared. - import sys -from os.path import dirname +from os.path import dirname, islink, realpath from optparse import OptionParser +def ignore_prefixes(module): + """ Return a list of prefixes to ignore in the coverage report if + we want to completely skip `module`. + """ + # A function like that is needed because some GNU/Linux + # distributions, such a Ubuntu, really like to build link farm in + # /usr/lib in order to save a few bytes on the disk. + dirnames = [dirname(module.__file__)] + + pymod = module.__file__.rstrip("c") + if islink(pymod): + dirnames.append(dirname(realpath(pymod))) + return dirnames + def parse_opts(): - parser = OptionParser(usage="%prog [OPTIONS]", + parser = OptionParser(usage="%prog [OPTIONS]", description="run the distutils2 unittests") - parser.add_option("-q", "--quiet", help="do not print verbose messages", + parser.add_option("-q", "--quiet", help="do not print verbose messages", action="store_true", default=False) parser.add_option("-c", "--coverage", action="store_true", default=False, help="produce a coverage report at the end of the run") parser.add_option("-r", "--report", action="store_true", default=False, help="produce a coverage report from the last test run") - parser.add_option("-m", "--show-missing", action="store_true", + parser.add_option("-m", "--show-missing", action="store_true", default=False, help=("Show line numbers of statements in each module " "that weren't executed.")) @@ -39,27 +42,38 @@ def coverage_report(opts): import coverage - import unittest2 - import docutils + from distutils2.tests.support import unittest cov = coverage.coverage() cov.load() - cov.report(omit_prefixes=["distutils2/tests", - "runtests", - "distutils2/_backport", - dirname(unittest2.__file__), - dirname(dirname(docutils.__file__))], - show_missing=opts.show_missing) + prefixes = ["runtests", "distutils2/tests", "distutils2/_backport"] + prefixes += ignore_prefixes(unittest) + + try: + import docutils + prefixes += ignore_prefixes(docutils) + except ImportError: + # that module is completely optional + pass + + try: + import roman + prefixes += ignore_prefixes(roman) + except ImportError: + # that module is also completely optional + pass + + cov.report(omit_prefixes=prefixes, show_missing=opts.show_missing) def test_main(): opts, args = parse_opts() verbose = not opts.quiet ret = 0 - + if opts.coverage or opts.report: import coverage - + if opts.coverage: cov = coverage.coverage() cov.erase() @@ -72,9 +86,9 @@ if opts.report or opts.coverage: coverage_report(opts) - + return ret - + def run_tests(verbose): import distutils2.tests from distutils2.tests import run_unittest, reap_children, TestFailed diff --git a/src/setup.py b/src/setup.py --- a/src/setup.py +++ b/src/setup.py @@ -11,7 +11,6 @@ from distutils2.compiler.ccompiler import new_compiler from distutils2.command.sdist import sdist from distutils2.command.install import install -from distutils2 import __version__ as VERSION from distutils2.util import find_packages f = open('README.txt') -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:48 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:48 +0200 Subject: [Python-checkins] distutils2: Removed deprecated and unused check_metadata() method. Message-ID: tarek.ziade pushed 6c333edc198e to distutils2: http://hg.python.org/distutils2/rev/6c333edc198e changeset: 517:6c333edc198e user: Jeremy Kloth date: Sat Jul 17 19:32:30 2010 -0600 summary: Removed deprecated and unused check_metadata() method. files: src/distutils2/command/sdist.py diff --git a/src/distutils2/command/sdist.py b/src/distutils2/command/sdist.py --- a/src/distutils2/command/sdist.py +++ b/src/distutils2/command/sdist.py @@ -171,14 +171,6 @@ # or zipfile, or whatever. self.make_distribution() - def check_metadata(self): - """Deprecated API.""" - warn("distutils.command.sdist.check_metadata is deprecated, \ - use the check command instead", PendingDeprecationWarning) - check = self.distribution.get_command_obj('check') - check.ensure_finalized() - check.run() - def get_file_list(self): """Figure out the list of files to include in the source distribution, and put it in 'self.filelist'. This might involve -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:48 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:48 +0200 Subject: [Python-checkins] distutils2: `sdist.add_defaults()` now uses source files from all commands. Message-ID: tarek.ziade pushed 32f887448cb0 to distutils2: http://hg.python.org/distutils2/rev/32f887448cb0 changeset: 516:32f887448cb0 parent: 367:70b98f177413 user: Jeremy Kloth date: Wed Jul 14 10:42:25 2010 -0600 summary: `sdist.add_defaults()` now uses source files from all commands. files: src/distutils2/command/build_py.py, src/distutils2/command/install_data.py, src/distutils2/command/sdist.py, src/distutils2/dist.py diff --git a/src/distutils2/command/build_py.py b/src/distutils2/command/build_py.py --- a/src/distutils2/command/build_py.py +++ b/src/distutils2/command/build_py.py @@ -372,7 +372,13 @@ return modules def get_source_files(self): - return [module[-1] for module in self.find_all_modules()] + sources = [module[-1] for module in self.find_all_modules()] + sources += [ + os.path.join(src_dir, filename) + for package, src_dir, build_dir, filenames in self.data_files + for filename in filenames + ] + return sources def get_module_outfile(self, build_dir, package, module): outfile_path = [build_dir] + list(package) + [module + ".py"] diff --git a/src/distutils2/command/install_data.py b/src/distutils2/command/install_data.py --- a/src/distutils2/command/install_data.py +++ b/src/distutils2/command/install_data.py @@ -74,6 +74,21 @@ (out, _) = self.copy_file(data, dir) self.outfiles.append(out) + def get_source_files(self): + sources = [] + for item in self.data_files: + if isinstance(item, str): # plain file + item = convert_path(item) + if os.path.isfile(item): + sources.append(item) + else: # a (dirname, filenames) tuple + dirname, filenames = item + for f in filenames: + f = convert_path(f) + if os.path.isfile(f): + sources.append(f) + return sources + def get_inputs(self): return self.data_files or [] diff --git a/src/distutils2/command/sdist.py b/src/distutils2/command/sdist.py --- a/src/distutils2/command/sdist.py +++ b/src/distutils2/command/sdist.py @@ -244,47 +244,10 @@ if files: self.filelist.extend(files) - # build_py is used to get: - # - python modules - # - files defined in package_data - build_py = self.get_finalized_command('build_py') - - # getting python files - if self.distribution.has_pure_modules(): - self.filelist.extend(build_py.get_source_files()) - - # getting package_data files - # (computed in build_py.data_files by build_py.finalize_options) - for pkg, src_dir, build_dir, filenames in build_py.data_files: - for filename in filenames: - self.filelist.append(os.path.join(src_dir, filename)) - - # getting distribution.data_files - if self.distribution.has_data_files(): - for item in self.distribution.data_files: - if isinstance(item, str): # plain file - item = convert_path(item) - if os.path.isfile(item): - self.filelist.append(item) - else: # a (dirname, filenames) tuple - dirname, filenames = item - for f in filenames: - f = convert_path(f) - if os.path.isfile(f): - self.filelist.append(f) - - if self.distribution.has_ext_modules(): - build_ext = self.get_finalized_command('build_ext') - self.filelist.extend(build_ext.get_source_files()) - - if self.distribution.has_c_libraries(): - build_clib = self.get_finalized_command('build_clib') - self.filelist.extend(build_clib.get_source_files()) - - if self.distribution.has_scripts(): - build_scripts = self.get_finalized_command('build_scripts') - self.filelist.extend(build_scripts.get_source_files()) - + for cmd_name in self.distribution.get_command_names(): + cmd_obj = self.get_finalized_command(cmd_name) + self.filelist.extend(cmd_obj.get_source_files()) + def prune_file_list(self): """Prune off branches that might slip into the file list as created diff --git a/src/distutils2/dist.py b/src/distutils2/dist.py --- a/src/distutils2/dist.py +++ b/src/distutils2/dist.py @@ -197,18 +197,18 @@ # These options are really the business of various commands, rather # than of the Distribution itself. We provide aliases for them in # Distribution as a convenience to the developer. - self.packages = None + self.packages = [] self.package_data = {} self.package_dir = None - self.py_modules = None - self.libraries = None - self.headers = None - self.ext_modules = None + self.py_modules = [] + self.libraries = [] + self.headers = [] + self.ext_modules = [] self.ext_package = None - self.include_dirs = None + self.include_dirs = [] self.extra_path = None - self.scripts = None - self.data_files = None + self.scripts = [] + self.data_files = [] self.password = '' self.use_2to3 = False self.convert_2to3_doctests = [] @@ -700,6 +700,24 @@ print(" %-*s %s" % (max_length, cmd, description)) + def _get_command_groups(self): + """Helper function to retrieve all the command class names divided + into "standard commands" (listed in distutils2.command.__all__) + and "extra commands" (mentioned in self.cmdclass, but not a standard + command). + """ + import distutils2.command + std_commands = distutils2.command.__all__ + is_std = {} + for cmd in std_commands: + is_std[cmd] = 1 + + extra_commands = [] + for cmd in self.cmdclass: + if not is_std.get(cmd): + extra_commands.append(cmd) + return std_commands, extra_commands + def print_commands(self): """Print out a help message listing all available commands with a description of each. The list is divided into "standard commands" @@ -708,17 +726,7 @@ descriptions come from the command class attribute 'description'. """ - import distutils2.command - std_commands = distutils2.command.__all__ - is_std = {} - for cmd in std_commands: - is_std[cmd] = 1 - - extra_commands = [] - for cmd in self.cmdclass.keys(): - if not is_std.get(cmd): - extra_commands.append(cmd) - + std_commands, extra_commands = self._get_command_groups() max_length = 0 for cmd in (std_commands + extra_commands): if len(cmd) > max_length: @@ -743,22 +751,8 @@ # Currently this is only used on Mac OS, for the Mac-only GUI # Distutils interface (by Jack Jansen) - import distutils2.command - std_commands = distutils2.command.__all__ - is_std = {} - for cmd in std_commands: - is_std[cmd] = 1 - - extra_commands = [] - for cmd in self.cmdclass.keys(): - if not is_std.get(cmd): - extra_commands.append(cmd) - rv = [] - for cmd in (std_commands + extra_commands): - cls = self.cmdclass.get(cmd) - if not cls: - cls = self.get_command_class(cmd) + for cls in self.get_command_classes(): try: description = cls.description except AttributeError: @@ -780,6 +774,23 @@ self.command_packages = pkgs return pkgs + def get_command_names(self): + """Return a list of command names.""" + return [getattr(cls, 'command_name', cls.__name__) + for cls in self.get_command_classes()] + + def get_command_classes(self): + """Return a list of all command classes.""" + std_commands, extra_commands = self._get_command_groups() + classes = [] + for cmd in (std_commands + extra_commands): + try: + cls = self.cmdclass[cmd] + except KeyError: + cls = self.get_command_class(cmd) + classes.append(cls) + return classes + def get_command_class(self, command): """Return the class that implements the Distutils command named by 'command'. First we check the 'cmdclass' dictionary; if the @@ -818,7 +829,6 @@ raise DistutilsModuleError("invalid command '%s'" % command) - def get_command_obj(self, command, create=1): """Return the command object for 'command'. Normally this object is cached on a previous call to 'get_command_obj()'; if no command -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:48 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:48 +0200 Subject: [Python-checkins] distutils2: Style nitpicks Message-ID: tarek.ziade pushed e51ba85f26cd to distutils2: http://hg.python.org/distutils2/rev/e51ba85f26cd changeset: 521:e51ba85f26cd tag: tip user: ?ric Araujo date: Sun Aug 08 02:48:27 2010 +0200 summary: Style nitpicks files: src/distutils2/command/build_py.py, src/distutils2/command/sdist.py, src/distutils2/dist.py diff --git a/src/distutils2/command/build_py.py b/src/distutils2/command/build_py.py --- a/src/distutils2/command/build_py.py +++ b/src/distutils2/command/build_py.py @@ -375,8 +375,7 @@ sources += [ os.path.join(src_dir, filename) for package, src_dir, build_dir, filenames in self.data_files - for filename in filenames - ] + for filename in filenames] return sources def get_module_outfile(self, build_dir, package, module): @@ -399,8 +398,7 @@ outputs += [ os.path.join(build_dir, filename) for package, src_dir, build_dir, filenames in self.data_files - for filename in filenames - ] + for filename in filenames] return outputs diff --git a/src/distutils2/command/sdist.py b/src/distutils2/command/sdist.py --- a/src/distutils2/command/sdist.py +++ b/src/distutils2/command/sdist.py @@ -238,7 +238,6 @@ for cmd_name in self.distribution.get_command_names(): cmd_obj = self.get_finalized_command(cmd_name) self.filelist.extend(cmd_obj.get_source_files()) - def prune_file_list(self): """Prune off branches that might slip into the file list as created diff --git a/src/distutils2/dist.py b/src/distutils2/dist.py --- a/src/distutils2/dist.py +++ b/src/distutils2/dist.py @@ -699,27 +699,20 @@ def _get_command_groups(self): """Helper function to retrieve all the command class names divided - into "standard commands" (listed in distutils2.command.__all__) - and "extra commands" (mentioned in self.cmdclass, but not a standard - command). + into standard commands (listed in distutils2.command.__all__) + and extra commands (given in self.cmdclass and not standard + commands). """ - import distutils2.command - std_commands = distutils2.command.__all__ - is_std = {} - for cmd in std_commands: - is_std[cmd] = 1 - - extra_commands = [] - for cmd in self.cmdclass: - if not is_std.get(cmd): - extra_commands.append(cmd) + from distutils2.command import __all__ as std_commands + extra_commands = [cmd for cmd in self.cmdclass + if cmd not in std_commands] return std_commands, extra_commands def print_commands(self): """Print out a help message listing all available commands with a - description of each. The list is divided into "standard commands" - (listed in distutils2.command.__all__) and "extra commands" - (mentioned in self.cmdclass, but not a standard command). The + description of each. The list is divided into standard commands + (listed in distutils2.command.__all__) and extra commands + (given in self.cmdclass and not standard commands). The descriptions come from the command class attribute 'description'. """ @@ -740,9 +733,10 @@ def get_command_list(self): """Get a list of (command, description) tuples. - The list is divided into "standard commands" (listed in - distutils2.command.__all__) and "extra commands" (mentioned in - self.cmdclass, but not a standard command). The descriptions come + + The list is divided into standard commands (listed in + distutils2.command.__all__) and extra commands (given in + self.cmdclass and not standard commands). The descriptions come from the command class attribute 'description'. """ # Currently this is only used on Mac OS, for the Mac-only GUI @@ -772,7 +766,7 @@ return pkgs def get_command_names(self): - """Return a list of command names.""" + """Return a list of all command names.""" return [getattr(cls, 'command_name', cls.__name__) for cls in self.get_command_classes()] -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:50:48 2010 From: python-checkins at python.org (tarek.ziade) Date: Sun, 08 Aug 2010 11:50:48 +0200 Subject: [Python-checkins] distutils2: Merge from Jeremy Message-ID: tarek.ziade pushed f96e8b6c97ba to distutils2: http://hg.python.org/distutils2/rev/f96e8b6c97ba changeset: 520:f96e8b6c97ba parent: 515:7c9f24dc9fbc parent: 519:8dc26ef8235d user: ?ric Araujo date: Sun Aug 08 02:33:52 2010 +0200 summary: Merge from Jeremy files: src/distutils2/command/build_py.py, src/distutils2/command/install_data.py, src/distutils2/command/sdist.py, src/distutils2/dist.py diff --git a/src/distutils2/command/build_py.py b/src/distutils2/command/build_py.py --- a/src/distutils2/command/build_py.py +++ b/src/distutils2/command/build_py.py @@ -371,7 +371,13 @@ return modules def get_source_files(self): - return [module[-1] for module in self.find_all_modules()] + sources = [module[-1] for module in self.find_all_modules()] + sources += [ + os.path.join(src_dir, filename) + for package, src_dir, build_dir, filenames in self.data_files + for filename in filenames + ] + return sources def get_module_outfile(self, build_dir, package, module): outfile_path = [build_dir] + list(package) + [module + ".py"] diff --git a/src/distutils2/command/install_data.py b/src/distutils2/command/install_data.py --- a/src/distutils2/command/install_data.py +++ b/src/distutils2/command/install_data.py @@ -72,6 +72,21 @@ (out, _) = self.copy_file(data, dir) self.outfiles.append(out) + def get_source_files(self): + sources = [] + for item in self.data_files: + if isinstance(item, str): # plain file + item = convert_path(item) + if os.path.isfile(item): + sources.append(item) + else: # a (dirname, filenames) tuple + dirname, filenames = item + for f in filenames: + f = convert_path(f) + if os.path.isfile(f): + sources.append(f) + return sources + def get_inputs(self): return self.data_files or [] diff --git a/src/distutils2/command/sdist.py b/src/distutils2/command/sdist.py --- a/src/distutils2/command/sdist.py +++ b/src/distutils2/command/sdist.py @@ -170,14 +170,6 @@ # or zipfile, or whatever. self.make_distribution() - def check_metadata(self): - """Deprecated API.""" - warn("distutils.command.sdist.check_metadata is deprecated, \ - use the check command instead", PendingDeprecationWarning) - check = self.distribution.get_command_obj('check') - check.ensure_finalized() - check.run() - def get_file_list(self): """Figure out the list of files to include in the source distribution, and put it in 'self.filelist'. This might involve @@ -243,47 +235,10 @@ if files: self.filelist.extend(files) - # build_py is used to get: - # - python modules - # - files defined in package_data - build_py = self.get_finalized_command('build_py') - - # getting python files - if self.distribution.has_pure_modules(): - self.filelist.extend(build_py.get_source_files()) - - # getting package_data files - # (computed in build_py.data_files by build_py.finalize_options) - for pkg, src_dir, build_dir, filenames in build_py.data_files: - for filename in filenames: - self.filelist.append(os.path.join(src_dir, filename)) - - # getting distribution.data_files - if self.distribution.has_data_files(): - for item in self.distribution.data_files: - if isinstance(item, str): # plain file - item = convert_path(item) - if os.path.isfile(item): - self.filelist.append(item) - else: # a (dirname, filenames) tuple - dirname, filenames = item - for f in filenames: - f = convert_path(f) - if os.path.isfile(f): - self.filelist.append(f) - - if self.distribution.has_ext_modules(): - build_ext = self.get_finalized_command('build_ext') - self.filelist.extend(build_ext.get_source_files()) - - if self.distribution.has_c_libraries(): - build_clib = self.get_finalized_command('build_clib') - self.filelist.extend(build_clib.get_source_files()) - - if self.distribution.has_scripts(): - build_scripts = self.get_finalized_command('build_scripts') - self.filelist.extend(build_scripts.get_source_files()) - + for cmd_name in self.distribution.get_command_names(): + cmd_obj = self.get_finalized_command(cmd_name) + self.filelist.extend(cmd_obj.get_source_files()) + def prune_file_list(self): """Prune off branches that might slip into the file list as created diff --git a/src/distutils2/dist.py b/src/distutils2/dist.py --- a/src/distutils2/dist.py +++ b/src/distutils2/dist.py @@ -187,18 +187,18 @@ # These options are really the business of various commands, rather # than of the Distribution itself. We provide aliases for them in # Distribution as a convenience to the developer. - self.packages = None + self.packages = [] self.package_data = {} self.package_dir = None - self.py_modules = None - self.libraries = None - self.headers = None - self.ext_modules = None + self.py_modules = [] + self.libraries = [] + self.headers = [] + self.ext_modules = [] self.ext_package = None - self.include_dirs = None + self.include_dirs = [] self.extra_path = None - self.scripts = None - self.data_files = None + self.scripts = [] + self.data_files = [] self.password = '' self.use_2to3 = False self.convert_2to3_doctests = [] @@ -697,6 +697,24 @@ print(" %-*s %s" % (max_length, cmd, description)) + def _get_command_groups(self): + """Helper function to retrieve all the command class names divided + into "standard commands" (listed in distutils2.command.__all__) + and "extra commands" (mentioned in self.cmdclass, but not a standard + command). + """ + import distutils2.command + std_commands = distutils2.command.__all__ + is_std = {} + for cmd in std_commands: + is_std[cmd] = 1 + + extra_commands = [] + for cmd in self.cmdclass: + if not is_std.get(cmd): + extra_commands.append(cmd) + return std_commands, extra_commands + def print_commands(self): """Print out a help message listing all available commands with a description of each. The list is divided into "standard commands" @@ -705,17 +723,7 @@ descriptions come from the command class attribute 'description'. """ - import distutils2.command - std_commands = distutils2.command.__all__ - is_std = {} - for cmd in std_commands: - is_std[cmd] = 1 - - extra_commands = [] - for cmd in self.cmdclass.keys(): - if not is_std.get(cmd): - extra_commands.append(cmd) - + std_commands, extra_commands = self._get_command_groups() max_length = 0 for cmd in (std_commands + extra_commands): if len(cmd) > max_length: @@ -740,22 +748,8 @@ # Currently this is only used on Mac OS, for the Mac-only GUI # Distutils interface (by Jack Jansen) - import distutils2.command - std_commands = distutils2.command.__all__ - is_std = {} - for cmd in std_commands: - is_std[cmd] = 1 - - extra_commands = [] - for cmd in self.cmdclass.keys(): - if not is_std.get(cmd): - extra_commands.append(cmd) - rv = [] - for cmd in (std_commands + extra_commands): - cls = self.cmdclass.get(cmd) - if not cls: - cls = self.get_command_class(cmd) + for cls in self.get_command_classes(): try: description = cls.description except AttributeError: @@ -777,6 +771,23 @@ self.command_packages = pkgs return pkgs + def get_command_names(self): + """Return a list of command names.""" + return [getattr(cls, 'command_name', cls.__name__) + for cls in self.get_command_classes()] + + def get_command_classes(self): + """Return a list of all command classes.""" + std_commands, extra_commands = self._get_command_groups() + classes = [] + for cmd in (std_commands + extra_commands): + try: + cls = self.cmdclass[cmd] + except KeyError: + cls = self.get_command_class(cmd) + classes.append(cls) + return classes + def get_command_class(self, command): """Return the class that implements the Distutils command named by 'command'. First we check the 'cmdclass' dictionary; if the -- Repository URL: http://hg.python.org/distutils2 From python-checkins at python.org Sun Aug 8 11:56:24 2010 From: python-checkins at python.org (mark.dickinson) Date: Sun, 8 Aug 2010 11:56:24 +0200 (CEST) Subject: [Python-checkins] r83812 - python/branches/py3k-dtoa/Lib/test/test_float.py Message-ID: <20100808095624.DB6D9EEA75@mail.python.org> Author: mark.dickinson Date: Sun Aug 8 11:56:24 2010 New Revision: 83812 Log: Make sure to restore old rounding mode in test_float. Modified: python/branches/py3k-dtoa/Lib/test/test_float.py Modified: python/branches/py3k-dtoa/Lib/test/test_float.py ============================================================================== --- python/branches/py3k-dtoa/Lib/test/test_float.py (original) +++ python/branches/py3k-dtoa/Lib/test/test_float.py Sun Aug 8 11:56:24 2010 @@ -394,9 +394,11 @@ # to set and get rounding mode modes = "tonearest", "upward", "downward", "towardzero" for mode in modes: + oldmode = float.__getround__() float.__setround__(mode) self.assertEqual(float.__getround__(), mode) - + float.__setround__(oldmode) + self.assertEqual(float.__getround__(), oldmode) @requires_setformat class FormatFunctionsTestCase(unittest.TestCase): From python-checkins at python.org Sun Aug 8 11:57:27 2010 From: python-checkins at python.org (mark.dickinson) Date: Sun, 8 Aug 2010 11:57:27 +0200 (CEST) Subject: [Python-checkins] r83813 - in python/branches/py3k-dtoa: Lib/test/test_strtod.py Python/dtoa.c Message-ID: <20100808095727.C6CF1EE98F@mail.python.org> Author: mark.dickinson Date: Sun Aug 8 11:57:27 2010 New Revision: 83813 Log: Issue #9009: Rewrite _Py_dg_strtod. See the issue tracker for details. Modified: python/branches/py3k-dtoa/Lib/test/test_strtod.py python/branches/py3k-dtoa/Python/dtoa.c Modified: python/branches/py3k-dtoa/Lib/test/test_strtod.py ============================================================================== --- python/branches/py3k-dtoa/Lib/test/test_strtod.py (original) +++ python/branches/py3k-dtoa/Lib/test/test_strtod.py Sun Aug 8 11:57:27 2010 @@ -82,7 +82,7 @@ hexdigs, e + 4*hexdigs) -TEST_SIZE = 1000 +TEST_SIZE = 10 class StrtodTests(unittest.TestCase): def check_strtod(self, s): @@ -457,14 +457,42 @@ '168444365510704342711559699508093042880177904174497792.001', # 1 - 2**-54, +-tiny '999999999999999944488848768742172978818416595458984375e-54', - '9999999999999999444888487687421729788184165954589843749999999e-54', - '9999999999999999444888487687421729788184165954589843750000001e-54', + '999999999999999944488848768742172978818416595458984' #... + '3749999999e-54', + '999999999999999944488848768742172978818416595458984' #... + '3750000001e-54', + # Values near underflow-to-zero boundary; these ended up + # as 0.0 instead of -0.0 (or vice versa) with some + # nonstandard rounding modes. + '-20727364621e-334', + '247032822920623272088284396434110686182529901307162' #... + '38221279284125033775362990e-400', ] for s in test_strings: self.check_strtod(s) + +def test_with_rounding_mode(mode): + class StrtodTestsWithRounding(StrtodTests): + def setUp(self): + StrtodTests.setUp(self) + self.old_mode = float.__getround__() + float.__setround__(mode) + + def tearDown(self): + float.__setround__(self.old_mode) + StrtodTests.tearDown(self) + + StrtodTestsWithRounding.__name__ = "StrtodTestsWith{}Rounding".format( + mode.capitalize()) + return StrtodTestsWithRounding + def test_main(): - test.support.run_unittest(StrtodTests) + test_classes = [StrtodTests] + if hasattr(float, '__setround__'): + for mode in "upward", "downward", "towardzero", "tonearest": + test_classes.append(test_with_rounding_mode(mode)) + test.support.run_unittest(*test_classes) if __name__ == "__main__": test_main() Modified: python/branches/py3k-dtoa/Python/dtoa.c ============================================================================== --- python/branches/py3k-dtoa/Python/dtoa.c (original) +++ python/branches/py3k-dtoa/Python/dtoa.c Sun Aug 8 11:57:27 2010 @@ -185,20 +185,23 @@ extern "C" { #endif -typedef union { double d; ULong L[2]; } U; +typedef union { ULong L[2]; double d; } U; #ifdef IEEE_8087 #define word0(x) (x)->L[1] #define word1(x) (x)->L[0] +#define Uint(hi, lo) {{lo, hi}} #else #define word0(x) (x)->L[0] #define word1(x) (x)->L[1] +#define Uint(hi, lo) {{hi, lo}} #endif #define dval(x) (x)->d -#ifndef STRTOD_DIGLIM -#define STRTOD_DIGLIM 40 -#endif +/* STRTOD_DIGLIM = 17 + 8. 17 is the number of decimal digits required to + distinguish doubles; the extra 8 digits ensure that truncation to + STRTOD_DIGLIM digits induces an error of at most 1e-8 ulps. */ +#define STRTOD_DIGLIM 25 /* maximum permitted exponent value for strtod; exponents larger than MAX_ABS_EXP in absolute value get truncated to +-MAX_ABS_EXP. MAX_ABS_EXP @@ -254,6 +257,13 @@ #define Quick_max 14 #define Int_max 14 +/* Some double constants, defined via the union U to avoid the chicken-and-egg + problem of relying on the compiler to do correctly-rounded string->double + conversions. */ +static U Dbl_min = Uint((Bias + Emin)*Exp_msk1, 0); /* 2.0 ** Emin */ +static U Exp4P = Uint((Bias + 2*P)*Exp_msk1, 0); /* 2.0**(2*P) */ +static U Inf = Uint(Exp_mask, 0); + #ifndef Flt_Rounds #ifdef FLT_ROUNDS #define Flt_Rounds FLT_ROUNDS @@ -970,21 +980,6 @@ return c; } -/* Given a positive normal double x, return the difference between x and the - next double up. Doesn't give correct results for subnormals. */ - -static double -ulp(U *x) -{ - Long L; - U u; - - L = (word0(x) & Exp_mask) - (P-1)*Exp_msk1; - word0(&u) = L; - word1(&u) = 0; - return dval(&u); -} - /* Convert a Bigint a to a double giving the value a / 2**(32 * a->wds). Error < 0.75 ulps. This function is currently used only by ratio. */ @@ -1155,14 +1150,8 @@ 1e20, 1e21, 1e22 }; -static const double -bigtens[] = { 1e16, 1e32, 1e64, 1e128, 1e256 }; -static const double tinytens[] = { 1e-16, 1e-32, 1e-64, 1e-128, - 9007199254740992.*9007199254740992.e-256 - /* = 2^106 * 1e-256 */ -}; -/* The factor of 2^53 in tinytens[4] helps us avoid setting the underflow */ -/* flag unnecessarily. It leads to a song and dance at the end of strtod. */ +static const double bigtens[] = { 1e16, 1e32, 1e64, 1e128, 1e256 }; +static const double tinytens[] = { 1e-16, 1e-32, 1e-64, 1e-128, 1e-256 }; #define Scale_Bit 0x10 #define n_bigtens 5 @@ -1279,27 +1268,144 @@ return q; } -/* sulp(x) is a version of ulp(x) that takes bc.scale into account. - - Assuming that x is finite and nonnegative (positive zero is fine - here) and x / 2^bc.scale is exactly representable as a double, - sulp(x) is equivalent to 2^bc.scale * ulp(x / 2^bc.scale). */ +/* Round a finite double x to the nearest integer (in double format), rounding + * ties away from zero. This function is unaffected by FPU rounding mode. */ static double -sulp(U *x, BCinfo *bc) +rnd(double x) { U u; + int exp; + dval(&u) = x; - if (bc->scale && 2*P + 1 > (int)((word0(x) & Exp_mask) >> Exp_shift)) { - /* rv/2^bc->scale is subnormal */ - word0(&u) = (P+2)*Exp_msk1; + /* Extract unbiased exponent. */ + exp = (int)((word0(&u) & Exp_mask) >> Exp_shift) - Bias; + if (exp < 0) { + /* Absolute value of x is at most 1.0; result is +-0.0 if exp < -1, + and +-1.0 if exp == -1. */ + word0(&u) &= Sign_bit; + if (exp == -1) + word0(&u) |= Exp_1; word1(&u) = 0; - return u.d; } - else { - assert(word0(x) || word1(x)); /* x != 0.0 */ - return ulp(x); + else if (exp <= P - 34) { + /* Absolute value of x is in [1.0, 2**(P-33)). The bit with value 0.5 + is bit (P - 34 - exp) in the upper word. */ + ULong mask = 1UL << ((P - 34) - exp); + /* Zero out all bits with value < 0.5. */ + word1(&u) = 0; + word0(&u) &= -mask; + /* If bit with value 0.5 is set, round up. */ + if (word0(&u) & mask) + word0(&u) += mask; + } + else if (exp <= P - 2) { + /* Absolute value of x is in [2**(P-33), 2**(P-1)). The bit with + value 0.5 is bit (P - 2 - exp) in the lower word. */ + ULong mask = 1UL << ((P - 2) - exp); + word1(&u) &= -mask; + if (word1(&u) & mask) { + word1(&u) += mask; + if (word1(&u) < mask) + /* word1 addition overflowed; add carry into word0. */ + ++word0(&u); + } } + /* If exp >= P-1 then abs(x) >= 2**(P-1) and x is already + an integer, so there's nothing to do. */ + return dval(&u); +} + +/* In strtod, the current estimate is stored in a pair (x, scale) consisting + of a double x and an int scale, representing the value x / 2**scale. + + For a finite double x, sulp(x) returns 1 ulp(x), taking scale into account. + That is, sulp(x, scale) / 2**scale == ulp(x / 2**scale), where ulp(x) is the + difference between abs(x) and the next largest double. + + Logic in the strtod function ensures that the pair (x, scale) always + satisfies the following two conditions: + + (0) x / 2**scale is an integral multiple of 2**(Emin - P + 1). In other + words, if x / 2**scale is at most DBL_MAX then it's exactly + representable as a double. + + (1) Either x / 2**(P-1) >= 2**Emin, or scale >= P - 1. + + It follows from these conditions that on input, x is necessarily either + normal or zero, and that sulp(x, scale) is always positive and normal. + Moreover, if x is zero then scale >= P - 1. + +*/ + +static double +sulp(U *x, int scale) +{ + U u; + int e; + + /* Method: if 2**k <= abs(x / 2**scale) < 2**(k+1) then + + ulp(x / 2**scale) == max(2**Etiny, 2**k / 2**(P - 1)). + + We compute + + e = max(scale + 1, k + Bias + scale) + + Here k + Bias + scale corresponds exactly to the value given by the + exponent bits of x. From the conditions above, e > P - 1. Setting u = + 2**(e - Bias - P + 1) gives + + u / 2**scale == 2**(e - Bias - P + 1 - scale) + == 2**max(2 - P - Bias, k - P + 1) + == max(2**Etiny, 2**k / 2**(P - 1)). + == ulp(x / 2**scale). + + The code below also works, unaltered, with x == +-0.0, + returning u such that u / 2**scale == 2**Etiny. + */ + + e = (int)((word0(x) & Exp_mask) >> Exp_shift); + if (e < scale + 1) + e = scale + 1; + + assert(e > P - 1); + word0(&u) = (ULong)(e - P + 1) << Exp_shift; + word1(&u) = 0; + return dval(&u); +} + +/* Given a scaled double (as used in _Py_dg_strtod), find the next largest + boundary, using the same scale. If we're already on a boundary, return the + next one up. + + A *boundary* is either 0.0, or a power of 2 that's at least 2**(Emin + 1); + 2**Emin is not considered a boundary, because the spacing of consecutive + floats does not change at 2**Emin. */ + +double next_boundary(U *x, int scale) { + U u; + int e; + + e = (int)((word0(x) & Exp_mask) >> Exp_shift); + + if (e < scale + 1) + e = scale + 1; + word0(&u) = (ULong)(e + 1) << Exp_shift; + word1(&u) = 0; + return dval(&u); +} + +double last_boundary(U *x, int scale) { + U u; + int e; + + e = (int)((word0(x) & Exp_mask) >> Exp_shift); + if (e <= scale + 1) + e = 0; + word0(&u) = (ULong)e << Exp_shift; + word1(&u) = 0; + return dval(&u); } /* parse_numeric_string: Parse and validate a finite numeric string. @@ -1655,553 +1761,332 @@ Bfree(b); Bfree(d); if (dd > 0 || (dd == 0 && odd)) - dval(rv) += sulp(rv, bc); + dval(rv) += sulp(rv, bc->scale); return 0; } +/* Get current FPU rounding mode; same values as for FLT_ROUNDS + -1 for indeterminate + 0 for rounding towards zero + 1 for rounding towards nearest (with halves rounded towards even) + 2 for rounding upward (towards positive infinity) + 3 for rounding downward (towards negative infinity) +*/ + +static int +get_rounding_mode(void) +{ + /* this may not work on all systems; we should try a configure-time + test. */ + return FLT_ROUNDS; +} + double _Py_dg_strtod(const char *s00, char **se) { - int bb2, bb5, bbe, bd2, bd5, bs2, dsign, e, e1, error, i, j, k, odd, sign; - Py_ssize_t nd, nd0, pos; + int bbe, dsign, e, e1, e2, e5, error, i, k, scale, sign; + Py_ssize_t nd, nd0; char *s0; - double aadj, aadj1; - U aadj2, adj, rv, rv0; - ULong y, z; - Long L; + double aadj, aadj_int, scalefac, ulp; + U rv; + Bigint *bb, *bd, *bs, *delta; BCinfo bc; - Bigint *bb, *bb1, *bd, *bd0, *bs, *delta; - - dval(&rv) = 0.; error = parse_numeric_string(s00, se, &s0, &nd, &nd0, &e, &sign); if (error) goto parse_error; /* If all digits were zero, exit with return value +-0.0. */ - if (!nd) + if (!nd) { + dval(&rv) = 0.0; goto ret; + } - /* Overflow and underflow. */ + /* Catch cases of obvious overflow and underflow. */ if (e > Big_10_exp) goto ovfl; else if (e <= Tiny_10_exp) goto undfl; - /* Initial approximation based on first DBL_DIG+1 digits of the input. */ - k = nd < DBL_DIG + 1 ? nd : DBL_DIG + 1; - for (pos = 0; pos < k; pos++) - dval(&rv) = 10.0 * dval(&rv) + (s0[pos < nd0 ? pos : pos + 1] - '0'); + /* Compute an initial approximation of the form rv * 10 ** e1, where rv is + * the integer obtained from the first (up to) DBL_DIG digits of the + * input. */ + k = nd <= DBL_DIG ? nd : DBL_DIG; + dval(&rv) = 0.0; + for (i = 0; i < k; i++) + dval(&rv) = 10.0 * dval(&rv) + (s0[i < nd0 ? i : i + 1] - '0'); e1 = e - k; - /* If there are at most Dbl_dig significant digits in the input, then rv - is exact and there's a chance to compute the exact result with a single - floating-point multiplication or division. */ - if (nd <= DBL_DIG) { - if (!e1) - goto ret; - if (e1 > 0) { - if (e1 <= Ten_pmax) { - dval(&rv) *= tens[e1]; - goto ret; + /* If the input has at most DBL_DIG significant digits (that is, nd <= + * DBL_DIG), then the represented value is exactly +/- rv * 10 ** e1. In + * this case, if e1 is small enough *and* the FPU rounding mode matches the + * one we want, then we can compute a correctly rounded result with a + * single floating-point multiplication or division. */ + if (get_rounding_mode() == 1) { + if (nd <= DBL_DIG) { + if (e1 >= 0) { + if (e1 <= Ten_pmax) { + dval(&rv) *= tens[e1]; + goto ret; + } + i = DBL_DIG - nd; + if (e1 - i <= Ten_pmax) { + dval(&rv) *= tens[i]; + dval(&rv) *= tens[e1 - i]; + goto ret; + } } - i = DBL_DIG - nd; - if (e1 - i <= Ten_pmax) { - /* A fancier test would sometimes let us do - * this for larger i values. - */ - dval(&rv) *= tens[i]; - dval(&rv) *= tens[e1 - i]; + else if (-e1 <= Ten_pmax) { + dval(&rv) /= tens[-e1]; goto ret; } } - else if (-e1 <= Ten_pmax) { - dval(&rv) /= tens[-e1]; - goto ret; - } } - bc.scale = 0; - - /* Get starting approximation = rv * 10**e1 */ + /* The fast path above wasn't applicable, so at this point we have to do + * things the hard way. Here's an outline of the strategy. + * + * (1) Compute a double approximation 'rv' to the true value of the + * input. There are various sources of error in this approximation: + * it's based on the first (at most) DBL_DIG significant digits of the + * input, as computed above, so there's a truncation error involved; the + * floating-point arithmetic used to compute the approximation will also + * contribute error. 1000 ulps is a safe upper bound for the magnitude + * of the error, though it will be much smaller in most cases. + * + * (2) Use Bigint arithmetic to compute an approximation 'aadj' to ulps + * difference between rv and the true value. Again there are sources of + * error here: instead of computing with the true value, we compute with + * a value truncated to at most STRTOD_DIGLIM digits; this introduces an + * absolute error of at most 1e-8 to aadj. Rounding error may + * contribute a further absolute error of 1e-10. + * + * (3) Adjust rv based on the ulps difference computed above. In most + * cases, the result will be unambiguous and we can return after this + * step. But: + * + * (4) If the ulps difference value computed in (2) indicates that the + * true value lies very nearly halfway between two representable + * doubles, then because of the uncertainty in aadj itself we need to + * more work. In this case the decision is passed off to the 'bigcomp' + * function, which does a comparison with the original (not truncated) + * input value in order to figure out which way to round. + * + * If the value represented by the input is close to the limits of the + * range of the double type (i.e., either very large or very small), then + * it's convenient to using a scaling factor 'scalefac == 2**scale' to + * ensure that rv is normal and not too close to the overflow boundary; + * thus our approximation will actually be rv / 2**scale (or rv / scalefac) + * rather than rv itself. + */ + /* Get starting approximation, rv * 10**e1. For small inputs, rv is + * scaled by a factor of 2**(2*P); that is, we compute the value rv * + * 10**e1 * 2**(2*P). For large inputs, we scale in the opposite + * direction, computing rv * 10**e1 / 2**(2*P). */ + scale = 0; + scalefac = 1.0; if (e1 > 0) { - if ((i = e1 & 15)) - dval(&rv) *= tens[i]; - if (e1 &= ~15) { - if (e1 > DBL_MAX_10_EXP) - goto ovfl; - e1 >>= 4; - for(j = 0; e1 > 1; j++, e1 >>= 1) - if (e1 & 1) - dval(&rv) *= bigtens[j]; - /* The last multiplication could overflow. */ - word0(&rv) -= P*Exp_msk1; - dval(&rv) *= bigtens[j]; - if ((z = word0(&rv) & Exp_mask) - > Exp_msk1*(DBL_MAX_EXP+Bias-P)) - goto ovfl; - if (z > Exp_msk1*(DBL_MAX_EXP+Bias-1-P)) { - /* set to largest number */ - /* (Can't trust DBL_MAX) */ - word0(&rv) = Big0; - word1(&rv) = Big1; - } - else - word0(&rv) += P*Exp_msk1; - } + dval(&rv) *= tens[e1 & 15]; + e1 >>= 4; + if (e1 & Scale_Bit) { + scale = -2*P; + scalefac = 1.0 / dval(&Exp4P); + dval(&rv) *= scalefac; + } + for(i = 0; e1 > 0; i++, e1 >>= 1) + if (e1 & 1) + dval(&rv) *= bigtens[i]; } else if (e1 < 0) { - /* The input decimal value lies in [10**e1, 10**(e1+16)). - - If e1 <= -512, underflow immediately. - If e1 <= -256, set bc.scale to 2*P. + e1 = -e1; + dval(&rv) /= tens[e1 & 15]; + e1 >>= 4; + if (e1 & Scale_Bit) { + scale = 2*P; + scalefac = dval(&Exp4P); + dval(&rv) *= scalefac; + } + for(i = 0; e1 > 0; i++, e1 >>= 1) + if (e1 & 1) + dval(&rv) *= tinytens[i]; + } + + /* Some inequalities: + + -324 < e <= 309 (so 10**-324 <= dv < 10**309). + 1 <= k <= DBL_DIG (= 15). + e = e1 + k (using the value of e1 before entering the above loops) + scale > 0 iff e1 <= -256, and scale < 0 iff e1 >= 256 + + From these we deduce: + + If scale > 0 then 1e-324 <= dv < 1e-241. + If scale == 0 then 1e-255 <= dv < 1e+270. + If scale < 0 then 1e+256 <= dv < 1e+309. - So for input value < 1e-256, bc.scale is always set; - for input value >= 1e-240, bc.scale is never set. - For input values in [1e-256, 1e-240), bc.scale may or may - not be set. */ + */ - e1 = -e1; - if ((i = e1 & 15)) - dval(&rv) /= tens[i]; - if (e1 >>= 4) { - if (e1 >= 1 << n_bigtens) - goto undfl; - if (e1 & Scale_Bit) - bc.scale = 2*P; - for(j = 0; e1 > 0; j++, e1 >>= 1) - if (e1 & 1) - dval(&rv) *= tinytens[j]; - if (bc.scale && (j = 2*P + 1 - ((word0(&rv) & Exp_mask) - >> Exp_shift)) > 0) { - /* scaled rv is denormal; clear j low bits */ - if (j >= 32) { - word1(&rv) = 0; - if (j >= 53) - word0(&rv) = (P+2)*Exp_msk1; - else - word0(&rv) &= 0xffffffff << (j-32); - } - else - word1(&rv) &= 0xffffffff << j; - } - if (!dval(&rv)) - goto undfl; - } + /* Ensure that rv / 2**scale is exactly representable. This is an issue + * only if scale > 0 and rv / 2**scale is subnormal: rv itself will be + * normal in this case, so will have a full 53-bit significand, but rv / + * 2**scale won't have that many bits available. + * + * Evil trickery ahead: We want to round rv to an exact multiple of + * 2**scale * DBL_MIN * 2**(1 - P). Rather than doing this by hand, we + * persuade the FPU to do the rounding for us: assuming that rv < 2**scale + * DBL_MIN, we add 2**scale * DBL_MIN to rv. The result is in the binade + * [2**scale * DBL_MIN, 2 * 2**scale * DBL_MIN], and the representable + * floating-point numbers in this binade are exactly the multiples of + * 2**scale * DBL_MIN * 2**(1 - P), hence the result of the addition is the + * exact sum rounded to the nearest multiple of 2**scale * DBL_MIN * + * 2**(1-P). Subtracting 2**scale * DBL_MIN again gives the rounded rv + * value. + * + * Note that at this point rv is still just an approximation, so we don't + * care about discrepancies due to rounding mode. + */ + if (scale > 0 && dval(&rv) < dval(&Dbl_min) * scalefac) { + dval(&rv) += dval(&Dbl_min) * scalefac; + dval(&rv) -= dval(&Dbl_min) * scalefac; } - /* Now the hard part -- adjusting rv to the correct value.*/ + /* We now have our starting approximation rv / 2**scale. Note that it's + * not possible for rv to be *strictly* negative at this point, though it + * might be -0.0; this is dealt with towards the end of the function. + */ + assert(dval(&rv) >= 0.0); - /* Put digits into bd: true value = bd * 10^e */ + /* Step (2): Compute 'aadj', an (approximation to) ulps difference between + * the true value (after truncation to STRTOD_DIGLIM significant digits) + * and the current approximation rv / 2**scale. + * + * From this point onwards, all floating-point operations on rv that are + * performed have an exactly representable result. This ensures that the + * FPU rounding mode can't affect the result. + */ - bc.e0 = e; - bc.nd = nd; - bc.nd0 = nd0; /* Only needed if nd > STRTOD_DIGLIM, but done here */ - /* to silence an erroneous warning about bc.nd0 */ - /* possibly not being initialized. */ - if (nd > STRTOD_DIGLIM) { - /* ASSERT(STRTOD_DIGLIM >= 18); 18 == one more than the */ - /* minimum number of decimal digits to distinguish double values */ - /* in IEEE arithmetic. */ + /* Notation used in the comments: 'srv' is rv / 2**scale; 'tdv' is the + value represented by the input (after truncation); 'ulp' is ulp(srv). + We're aiming to compute (srv - tdv) / ulp. We do this by using integer + arithmetic to compute (M*srv - M*tdv) / (M*ulp) for a suitable scale + factor M for which M*srv, M*tdv and M*ulp are all integral. */ + + k = nd < STRTOD_DIGLIM ? nd : STRTOD_DIGLIM; + bd = s2b(s0, nd0, k); /* tdv == bd * 10**(e - k). */ + bb = sd2b(&rv, scale, &bbe); /* srv == bb * 2**bbe. */ + bs = i2b(1); /* ulp == 2**bbe. */ + if (bd == NULL || bb == NULL || bs == NULL) + goto failed_malloc; - /* Truncate input to 18 significant digits, then discard any trailing - zeros on the result. */ - nd = 18; - while (nd > 0 && s0[nd - 1 < nd0 ? nd - 1 : nd] == '0') - nd--; + /* srv, tdv and ulp are proportional to bb, bd * 2**(e-k-bbe)*5**(e-k) and + bs, respectively. Scale bb, bd, bs by the appropriate powers of 5... */ + e5 = e - k; + if (e5 > 0) + bd = pow5mult(bd, e5); + else if (e5 < 0) { + bb = pow5mult(bb, -e5); + bs = pow5mult(bs, -e5); } - bd0 = s2b(s0, nd0, nd); - if (bd0 == NULL) + if (bd == NULL || bb == NULL || bs == NULL) goto failed_malloc; - /* Notation for the comments below. Write: - - - dv for the absolute value of the number represented by the original - decimal input string. - - - if we've truncated dv, write tdv for the truncated value. - Otherwise, set tdv == dv. - - - srv for the quantity rv/2^bc.scale; so srv is the current binary - approximation to tdv (and dv). It should be exactly representable - in an IEEE 754 double. - */ - - for(;;) { - - /* This is the main correction loop for _Py_dg_strtod. - - We've got a decimal value tdv, and a floating-point approximation - srv=rv/2^bc.scale to tdv. The aim is to determine whether srv is - close enough (i.e., within 0.5 ulps) to tdv, and to compute a new - approximation if not. - - To determine whether srv is close enough to tdv, compute integers - bd, bb and bs proportional to tdv, srv and 0.5 ulp(srv) - respectively, and then use integer arithmetic to determine whether - |tdv - srv| is less than, equal to, or greater than 0.5 ulp(srv). - */ - - bd = Balloc(bd0->k); - if (bd == NULL) { - Bfree(bd0); - goto failed_malloc; - } - Bcopy(bd, bd0); - bb = sd2b(&rv, bc.scale, &bbe); /* srv = bb * 2^bbe */ - if (bb == NULL) { - Bfree(bd); - Bfree(bd0); - goto failed_malloc; - } - /* Record whether lsb of bb is odd, in case we need this - for the round-to-even step later. */ - odd = bb->x[0] & 1; - - /* tdv = bd * 10**e; srv = bb * 2**bbe */ - bs = i2b(1); - if (bs == NULL) { - Bfree(bb); - Bfree(bd); - Bfree(bd0); - goto failed_malloc; - } - - if (e >= nd) { - bb2 = bb5 = 0; - bd2 = bd5 = e - nd; - } - else { - bb2 = bb5 = nd - e; - bd2 = bd5 = 0; - } - if (bbe >= 0) - bb2 += bbe; - else - bd2 -= bbe; - bs2 = bb2; - bb2++; - bd2++; - - /* At this stage bd5 - bb5 == e == bd2 - bb2 + bbe, bb2 - bs2 == 1, - and bs == 1, so: - - tdv == bd * 10**e = bd * 2**(bbe - bb2 + bd2) * 5**(bd5 - bb5) - srv == bb * 2**bbe = bb * 2**(bbe - bb2 + bb2) - 0.5 ulp(srv) == 2**(bbe-1) = bs * 2**(bbe - bb2 + bs2) - - It follows that: - - M * tdv = bd * 2**bd2 * 5**bd5 - M * srv = bb * 2**bb2 * 5**bb5 - M * 0.5 ulp(srv) = bs * 2**bs2 * 5**bb5 - - for some constant M. (Actually, M == 2**(bb2 - bbe) * 5**bb5, but - this fact is not needed below.) - */ - - /* Remove factor of 2**i, where i = min(bb2, bd2, bs2). */ - i = bb2 < bd2 ? bb2 : bd2; - if (i > bs2) - i = bs2; - if (i > 0) { - bb2 -= i; - bd2 -= i; - bs2 -= i; - } - - /* Scale bb, bd, bs by the appropriate powers of 2 and 5. */ - if (bb5 > 0) { - bs = pow5mult(bs, bb5); - if (bs == NULL) { - Bfree(bb); - Bfree(bd); - Bfree(bd0); - goto failed_malloc; - } - bb1 = mult(bs, bb); - Bfree(bb); - bb = bb1; - if (bb == NULL) { - Bfree(bs); - Bfree(bd); - Bfree(bd0); - goto failed_malloc; - } - } - if (bb2 > 0) { - bb = lshift(bb, bb2); - if (bb == NULL) { - Bfree(bs); - Bfree(bd); - Bfree(bd0); - goto failed_malloc; - } - } - if (bd5 > 0) { - bd = pow5mult(bd, bd5); - if (bd == NULL) { - Bfree(bb); - Bfree(bs); - Bfree(bd0); - goto failed_malloc; - } - } - if (bd2 > 0) { - bd = lshift(bd, bd2); - if (bd == NULL) { - Bfree(bb); - Bfree(bs); - Bfree(bd0); - goto failed_malloc; - } - } - if (bs2 > 0) { - bs = lshift(bs, bs2); - if (bs == NULL) { - Bfree(bb); - Bfree(bd); - Bfree(bd0); - goto failed_malloc; - } - } - - /* Now bd, bb and bs are scaled versions of tdv, srv and 0.5 ulp(srv), - respectively. Compute the difference |tdv - srv|, and compare - with 0.5 ulp(srv). */ - - delta = diff(bb, bd); - if (delta == NULL) { - Bfree(bb); - Bfree(bs); - Bfree(bd); - Bfree(bd0); - goto failed_malloc; - } - dsign = delta->sign; - delta->sign = 0; - i = cmp(delta, bs); - if (bc.nd > nd && i <= 0) { - if (dsign) - break; /* Must use bigcomp(). */ - - /* Here rv overestimates the truncated decimal value by at most - 0.5 ulp(rv). Hence rv either overestimates the true decimal - value by <= 0.5 ulp(rv), or underestimates it by some small - amount (< 0.1 ulp(rv)); either way, rv is within 0.5 ulps of - the true decimal value, so it's possible to exit. - - Exception: if scaled rv is a normal exact power of 2, but not - DBL_MIN, then rv - 0.5 ulp(rv) takes us all the way down to the - next double, so the correctly rounded result is either rv - 0.5 - ulp(rv) or rv; in this case, use bigcomp to distinguish. */ - - if (!word1(&rv) && !(word0(&rv) & Bndry_mask)) { - /* rv can't be 0, since it's an overestimate for some - nonzero value. So rv is a normal power of 2. */ - j = (int)(word0(&rv) & Exp_mask) >> Exp_shift; - /* rv / 2^bc.scale = 2^(j - 1023 - bc.scale); use bigcomp if - rv / 2^bc.scale >= 2^-1021. */ - if (j - bc.scale >= 2) { - dval(&rv) -= 0.5 * sulp(&rv, &bc); - break; /* Use bigcomp. */ - } - } + /* ... and powers of 2. */ + e2 = e - k - bbe; + if (e2 > 0) + bd = lshift(bd, e2); + else if (e2 < 0) { + bb = lshift(bb, -e2); + bs = lshift(bs, -e2); + } + if (bd == NULL || bb == NULL || bs == NULL) + goto failed_malloc; - { - bc.nd = nd; - i = -1; /* Discarded digits make delta smaller. */ - } - } + /* Now srv, tdv and ulp are proportional to bb, bd, and bs, respectively. + * Compute the quotient aadj = (srv - tdv) / ulp = (bb - bd) / bs, as a + * double. */ + delta = diff(bb, bd); + dsign = delta->sign; + if (delta == NULL) + goto failed_malloc; + aadj = ratio(delta, bs); + Bfree(delta); + if (dsign) + aadj = -aadj; + Bfree(bd); + Bfree(bb); + Bfree(bs); - if (i < 0) { - /* Error is less than half an ulp -- check for - * special case of mantissa a power of two. - */ - if (dsign || word1(&rv) || word0(&rv) & Bndry_mask - || (word0(&rv) & Exp_mask) <= (2*P+1)*Exp_msk1 - ) { - break; - } - if (!delta->x[0] && delta->wds <= 1) { - /* exact result */ - break; - } - delta = lshift(delta,Log2P); - if (delta == NULL) { - Bfree(bb); - Bfree(bs); - Bfree(bd); - Bfree(bd0); - goto failed_malloc; - } - if (cmp(delta, bs) > 0) - goto drop_down; - break; - } - if (i == 0) { - /* exactly half-way between */ - if (dsign) { - if ((word0(&rv) & Bndry_mask1) == Bndry_mask1 - && word1(&rv) == ( - (bc.scale && - (y = word0(&rv) & Exp_mask) <= 2*P*Exp_msk1) ? - (0xffffffff & (0xffffffff << (2*P+1-(y>>Exp_shift)))) : - 0xffffffff)) { - /*boundary case -- increment exponent*/ - word0(&rv) = (word0(&rv) & Exp_mask) - + Exp_msk1 - ; - word1(&rv) = 0; - dsign = 0; - break; - } - } - else if (!(word0(&rv) & Bndry_mask) && !word1(&rv)) { - drop_down: - /* boundary case -- decrement exponent */ - if (bc.scale) { - L = word0(&rv) & Exp_mask; - if (L <= (2*P+1)*Exp_msk1) { - if (L > (P+2)*Exp_msk1) - /* round even ==> */ - /* accept rv */ - break; - /* rv = smallest denormal */ - if (bc.nd > nd) - break; - goto undfl; - } - } - L = (word0(&rv) & Exp_mask) - Exp_msk1; - word0(&rv) = L | Bndry_mask1; - word1(&rv) = 0xffffffff; - break; - } - if (!odd) - break; - if (dsign) - dval(&rv) += sulp(&rv, &bc); - else { - dval(&rv) -= sulp(&rv, &bc); - if (!dval(&rv)) { - if (bc.nd >nd) - break; - goto undfl; - } - } - dsign = 1 - dsign; - break; - } - if ((aadj = ratio(delta, bs)) <= 2.) { - if (dsign) - aadj = aadj1 = 1.; - else if (word1(&rv) || word0(&rv) & Bndry_mask) { - if (word1(&rv) == Tiny1 && !word0(&rv)) { - if (bc.nd >nd) - break; - goto undfl; - } - aadj = 1.; - aadj1 = -1.; - } - else { - /* special case -- power of FLT_RADIX to be */ - /* rounded down... */ + /* Step (3): Adjust rv using the computed value of aadj. In most cases the + correct result is unambiguous; in hard cases we move on to step (4). */ - if (aadj < 2./FLT_RADIX) - aadj = 1./FLT_RADIX; - else - aadj *= 0.5; - aadj1 = -aadj; - } - } - else { + ulp = sulp(&rv, scale); + /* Invariant: here and after the adjustment, srv ~= tdv + ulp * aadj. */ + if (aadj <= 0.0) { + /* next = ulps from next boundary up to rv (will be negative) */ + double next = (dval(&rv) - next_boundary(&rv, scale))/ulp; + if (aadj <= next) { + /* Adjustment takes us past a power of 2 boundary, going up. */ aadj *= 0.5; - aadj1 = dsign ? aadj : -aadj; - if (Flt_Rounds == 0) - aadj1 += 0.5; - } - y = word0(&rv) & Exp_mask; - - /* Check for overflow */ - - if (y == Exp_msk1*(DBL_MAX_EXP+Bias-1)) { - dval(&rv0) = dval(&rv); - word0(&rv) -= P*Exp_msk1; - adj.d = aadj1 * ulp(&rv); - dval(&rv) += adj.d; - if ((word0(&rv) & Exp_mask) >= - Exp_msk1*(DBL_MAX_EXP+Bias-P)) { - if (word0(&rv0) == Big0 && word1(&rv0) == Big1) { - Bfree(bb); - Bfree(bd); - Bfree(bs); - Bfree(bd0); - Bfree(delta); - goto ovfl; - } - word0(&rv) = Big0; - word1(&rv) = Big1; - goto cont; - } - else - word0(&rv) += P*Exp_msk1; + next *= 0.5; + ulp *= 2.0; + /* Round to integer if next is even, else to half-odd-integer. */ + aadj_int = next + rnd(aadj - next); } else { - if (bc.scale && y <= 2*P*Exp_msk1) { - if (aadj <= 0x7fffffff) { - if ((z = (ULong)aadj) <= 0) - z = 1; - aadj = z; - aadj1 = dsign ? aadj : -aadj; - } - dval(&aadj2) = aadj1; - word0(&aadj2) += (2*P+1)*Exp_msk1 - y; - aadj1 = dval(&aadj2); - } - adj.d = aadj1 * ulp(&rv); - dval(&rv) += adj.d; - } - z = word0(&rv) & Exp_mask; - if (bc.nd == nd) { - if (!bc.scale) - if (y == z) { - /* Can we stop now? */ - L = (Long)aadj; - aadj -= L; - /* The tolerances below are conservative. */ - if (dsign || word1(&rv) || word0(&rv) & Bndry_mask) { - if (aadj < .4999999 || aadj > .5000001) - break; - } - else if (aadj < .4999999/FLT_RADIX) - break; - } + /* Usual case. */ + aadj_int = rnd(aadj); } - cont: - Bfree(bb); - Bfree(bd); - Bfree(bs); - Bfree(delta); } - Bfree(bb); - Bfree(bd); - Bfree(bs); - Bfree(bd0); - Bfree(delta); - if (bc.nd > nd) { + else { + double last = (dval(&rv) - last_boundary(&rv, scale))/ulp; + if (aadj > last && last_boundary(&rv, scale) != 0.0) { + /* Adjustment takes us past a power of 2 boundary, going down. */ + aadj *= 2.0; + ulp *= 0.5; + } + aadj_int = rnd(aadj); + } + + /* Adjust, and compute residual error. */ + dval(&rv) -= aadj_int * ulp; + aadj -= aadj_int; + + /* If we're in a near halfway case, use bigcomp to figure out + the correctly-rounded value */ + assert(aadj > -0.5000001 && aadj < 0.5000001); + if (aadj < -0.4999999 || aadj > 0.4999999) { + /* input to bigcomp should be the lower of the two possible results */ + if (aadj > 0.0) { + dval(&rv) -= ulp; + /* aadj += 1.0; */ /* Necessary only to maintain invariant; not + * used beyond this point. */ + } + bc.nd0 = nd0; + bc.nd = nd; + bc.e0 = e; + bc.scale = scale; error = bigcomp(&rv, s0, &bc); if (error) goto failed_malloc; } - if (bc.scale) { - word0(&rv0) = Exp_1 - 2*P*Exp_msk1; - word1(&rv0) = 0; - dval(&rv) *= dval(&rv0); - } + /* Under some rounding modes (particularly FE_DOWNWARD), it's possible for + * dval(&rv) to be -0.0 at this point; we want to get rid of the negative + * sign. */ + if (word0(&rv) & Sign_bit) + word0(&rv) &= ~Sign_bit; + + /* Do underflow and overflow checks, and undo the effects of scaling (if + * applicable). */ + if (word0(&rv) == 0 && word1(&rv) == 0) + goto undfl; + if ((int)((word0(&rv) & Exp_mask) >> Exp_shift) - scale > Emax + Bias) + goto ovfl; + + /* Undo the effects of the scaling, if applicable. */ + if (scale) + dval(&rv) /= scalefac; ret: return sign ? -dval(&rv) : dval(&rv); @@ -2210,6 +2095,9 @@ return 0.0; failed_malloc: + if (bd != NULL) Bfree(bd); + if (bb != NULL) Bfree(bb); + if (bs != NULL) Bfree(bs); errno = ENOMEM; return -1.0; @@ -2218,9 +2106,7 @@ ovfl: errno = ERANGE; - /* Can't trust HUGE_VAL */ - word0(&rv) = Exp_mask; - word1(&rv) = 0; + dval(&rv) = dval(&Inf); return sign ? -dval(&rv) : dval(&rv); } From python-checkins at python.org Sun Aug 8 12:41:31 2010 From: python-checkins at python.org (mark.dickinson) Date: Sun, 8 Aug 2010 12:41:31 +0200 (CEST) Subject: [Python-checkins] r83814 - in python/branches/py3k-dtoa: Demo/README Demo/embed/Makefile Demo/embed/demo.c Demo/embed/loop.c Demo/pdist Demo/scripts/README Demo/scripts/toaiff.py Demo/tkinter/guido/AttrDialog.py Demo/tkinter/guido/ManPage.py Demo/tkinter/guido/brownian2.py Demo/tkinter/guido/kill.py Demo/zlib/minigzip.py Demo/zlib/zlibdemo.py Doc/Makefile Doc/README.txt Doc/c-api/init.rst Doc/c-api/unicode.rst Doc/c-api/weakref.rst Doc/conf.py Doc/distutils/apiref.rst Doc/distutils/builtdist.rst Doc/distutils/commandref.rst Doc/distutils/examples.rst Doc/distutils/setupscript.rst Doc/distutils/sourcedist.rst Doc/distutils/uploading.rst Doc/documenting/building.rst Doc/documenting/markup.rst Doc/extending/extending.rst Doc/faq/extending.rst Doc/glossary.rst Doc/howto/descriptor.rst Doc/howto/doanddont.rst Doc/install/index.rst Doc/library/2to3.rst Doc/library/abc.rst Doc/library/argparse.rst Doc/library/ast.rst Doc/library/base64.rst Doc/library/bdb.rst Doc/library/binascii.rst Doc/library/bisect.rst Doc/library/cgi.rst Doc/library/cmath.rst Doc/library/cmd.rst Doc/library/collections.rst Doc/library/configparser.rst Doc/library/constants.rst Doc/library/contextlib.rst Doc/library/datetime.rst Doc/library/dbm.rst Doc/library/doctest.rst Doc/library/email.errors.rst Doc/library/fileinput.rst Doc/library/fnmatch.rst Doc/library/ftplib.rst Doc/library/functions.rst Doc/library/functools.rst Doc/library/heapq.rst Doc/library/html.parser.rst Doc/library/http.client.rst Doc/library/http.cookiejar.rst Doc/library/importlib.rst Doc/library/io.rst Doc/library/itertools.rst Doc/library/linecache.rst Doc/library/locale.rst Doc/library/math.rst Doc/library/mmap.rst Doc/library/multiprocessing.rst Doc/library/optparse.rst Doc/library/os.path.rst Doc/library/os.rst Doc/library/parser.rst Doc/library/pdb.rst Doc/library/pickletools.rst Doc/library/pyexpat.rst Doc/library/re.rst Doc/library/select.rst Doc/library/signal.rst Doc/library/smtpd.rst Doc/library/smtplib.rst Doc/library/socket.rst Doc/library/sqlite3.rst Doc/library/stdtypes.rst Doc/library/string.rst Doc/library/struct.rst Doc/library/subprocess.rst Doc/library/sys.rst Doc/library/test.rst Doc/library/threading.rst Doc/library/tkinter.rst Doc/library/unittest.rst Doc/library/urllib.parse.rst Doc/library/urllib.request.rst Doc/library/wsgiref.rst Doc/library/xml.dom.minidom.rst Doc/library/xml.dom.rst Doc/library/xml.sax.reader.rst Doc/library/zipfile.rst Doc/license.rst Doc/make.bat Doc/reference/compound_stmts.rst Doc/reference/expressions.rst Doc/reference/lexical_analysis.rst Doc/tools/sphinx-build.py Doc/tools/sphinxext/indexsidebar.html Doc/tools/sphinxext/pyspecific.py Doc/tutorial/classes.rst Doc/tutorial/datastructures.rst Doc/tutorial/floatingpoint.rst Doc/using/cmdline.rst Doc/whatsnew/2.0.rst Doc/whatsnew/2.4.rst Doc/whatsnew/2.5.rst Doc/whatsnew/2.7.rst Doc/whatsnew/3.2.rst Grammar/Grammar Include/Python.h Include/floatobject.h Include/patchlevel.h Include/pytime.h Include/structseq.h LICENSE Lib/_abcoll.py Lib/_strptime.py Lib/argparse.py Lib/ast.py Lib/asyncore.py Lib/base64.py Lib/bdb.py Lib/cProfile.py Lib/cmd.py Lib/collections.py Lib/configparser.py Lib/ctypes/__init__.py Lib/ctypes/test/test_arrays.py Lib/ctypes/test/test_bitfields.py Lib/ctypes/test/test_buffers.py Lib/ctypes/test/test_bytes.py Lib/ctypes/test/test_callbacks.py Lib/ctypes/test/test_cast.py Lib/ctypes/test/test_cfuncs.py Lib/ctypes/test/test_errno.py Lib/ctypes/test/test_internals.py Lib/ctypes/test/test_keeprefs.py Lib/ctypes/test/test_libc.py Lib/ctypes/test/test_loading.py Lib/ctypes/test/test_objects.py Lib/ctypes/test/test_parameters.py Lib/ctypes/test/test_prototypes.py Lib/ctypes/test/test_python_api.py Lib/ctypes/test/test_random_things.py Lib/ctypes/test/test_repr.py Lib/ctypes/test/test_returnfuncptrs.py Lib/ctypes/test/test_stringptr.py Lib/ctypes/test/test_strings.py Lib/ctypes/test/test_structures.py Lib/ctypes/test/test_unicode.py Lib/curses/wrapper.py Lib/datetime.py Lib/dbm/__init__.py Lib/decimal.py Lib/distutils/__init__.py Lib/distutils/archive_util.py Lib/distutils/bcppcompiler.py Lib/distutils/ccompiler.py Lib/distutils/cmd.py Lib/distutils/command/bdist.py Lib/distutils/command/bdist_dumb.py Lib/distutils/command/bdist_msi.py Lib/distutils/command/bdist_rpm.py Lib/distutils/command/bdist_wininst.py Lib/distutils/command/build.py Lib/distutils/command/build_clib.py Lib/distutils/command/build_ext.py Lib/distutils/command/build_py.py Lib/distutils/command/build_scripts.py Lib/distutils/command/config.py Lib/distutils/command/install.py Lib/distutils/command/register.py Lib/distutils/command/sdist.py Lib/distutils/command/upload.py Lib/distutils/config.py Lib/distutils/core.py Lib/distutils/cygwinccompiler.py Lib/distutils/dep_util.py Lib/distutils/dir_util.py Lib/distutils/dist.py Lib/distutils/emxccompiler.py Lib/distutils/errors.py Lib/distutils/extension.py Lib/distutils/fancy_getopt.py Lib/distutils/file_util.py Lib/distutils/filelist.py Lib/distutils/msvc9compiler.py Lib/distutils/msvccompiler.py Lib/distutils/sysconfig.py Lib/distutils/tests/support.py Lib/distutils/tests/test_archive_util.py Lib/distutils/tests/test_bdist.py Lib/distutils/tests/test_bdist_dumb.py Lib/distutils/tests/test_bdist_rpm.py Lib/distutils/tests/test_bdist_wininst.py Lib/distutils/tests/test_build_clib.py Lib/distutils/tests/test_build_ext.py Lib/distutils/tests/test_build_py.py Lib/distutils/tests/test_build_scripts.py Lib/distutils/tests/test_ccompiler.py Lib/distutils/tests/test_cmd.py Lib/distutils/tests/test_cygwinccompiler.py Lib/distutils/tests/test_dist.py Lib/distutils/tests/test_emxccompiler.py Lib/distutils/tests/test_extension.py Lib/distutils/tests/test_file_util.py Lib/distutils/tests/test_filelist.py Lib/distutils/tests/test_install.py Lib/distutils/tests/test_install_lib.py Lib/distutils/tests/test_log.py Lib/distutils/tests/test_register.py Lib/distutils/tests/test_sdist.py Lib/distutils/tests/test_sysconfig.py Lib/distutils/tests/test_unixccompiler.py Lib/distutils/tests/test_upload.py Lib/distutils/tests/test_util.py Lib/distutils/text_file.py Lib/distutils/unixccompiler.py Lib/distutils/util.py Lib/doctest.py Lib/email/base64mime.py Lib/email/feedparser.py Lib/email/header.py Lib/email/message.py Lib/email/test/test_email.py Lib/fileinput.py Lib/fnmatch.py Lib/functools.py Lib/getopt.py Lib/http/client.py Lib/http/cookiejar.py Lib/http/cookies.py Lib/http/server.py Lib/idlelib/NEWS.txt Lib/idlelib/PyShell.py Lib/idlelib/idlever.py Lib/imaplib.py Lib/importlib/test/benchmark.py Lib/json/__init__.py Lib/json/decoder.py Lib/json/encoder.py Lib/json/tests/test_fail.py Lib/mailbox.py Lib/multiprocessing/connection.py Lib/multiprocessing/forking.py Lib/multiprocessing/heap.py Lib/multiprocessing/reduction.py Lib/ntpath.py Lib/os.py Lib/pdb.doc Lib/pdb.py Lib/pickle.py Lib/pickletools.py Lib/platform.py Lib/posixpath.py Lib/profile.py Lib/pstats.py Lib/pydoc.py Lib/pydoc_data/topics.py Lib/re.py Lib/site.py Lib/smtpd.py Lib/smtplib.py Lib/sndhdr.py Lib/socket.py Lib/string.py Lib/subprocess.py Lib/sysconfig.py Lib/tarfile.py Lib/test/cfgparser.2 Lib/test/cfgparser.3 Lib/test/data/README Lib/test/datetimetester.py Lib/test/decimaltestdata/extra.decTest Lib/test/formatfloat_testcases.txt Lib/test/math_testcases.txt Lib/test/mock_socket.py Lib/test/pickletester.py Lib/test/regrtest.py Lib/test/script_helper.py Lib/test/sndhdrdata Lib/test/sortperf.py Lib/test/support.py Lib/test/symlink_support.py Lib/test/test_SimpleHTTPServer.py Lib/test/test_argparse.py Lib/test/test_array.py Lib/test/test_ast.py Lib/test/test_asyncore.py Lib/test/test_binascii.py Lib/test/test_binop.py Lib/test/test_bz2.py Lib/test/test_calendar.py Lib/test/test_cfgparser.py Lib/test/test_cmath.py Lib/test/test_cmd.py Lib/test/test_collections.py Lib/test/test_complex.py Lib/test/test_curses.py Lib/test/test_datetime.py Lib/test/test_decimal.py Lib/test/test_doctest.py Lib/test/test_fileinput.py Lib/test/test_float.py Lib/test/test_fnmatch.py Lib/test/test_functools.py Lib/test/test_gdb.py Lib/test/test_getopt.py Lib/test/test_glob.py Lib/test/test_http_cookiejar.py Lib/test/test_http_cookies.py Lib/test/test_httplib.py Lib/test/test_httpservers.py Lib/test/test_import.py Lib/test/test_math.py Lib/test/test_minidom.py Lib/test/test_mmap.py Lib/test/test_ntpath.py Lib/test/test_numeric_tower.py Lib/test/test_optparse.py Lib/test/test_os.py Lib/test/test_osx_env.py Lib/test/test_pdb.py Lib/test/test_platform.py Lib/test/test_posix.py Lib/test/test_posixpath.py Lib/test/test_pow.py Lib/test/test_profilehooks.py Lib/test/test_pstats.py Lib/test/test_queue.py Lib/test/test_re.py Lib/test/test_rlcompleter.py Lib/test/test_robotparser.py Lib/test/test_sax.py Lib/test/test_sched.py Lib/test/test_set.py Lib/test/test_shutil.py Lib/test/test_signal.py Lib/test/test_site.py Lib/test/test_smtpd.py Lib/test/test_smtplib.py Lib/test/test_sndhdr.py Lib/test/test_ssl.py Lib/test/test_struct.py Lib/test/test_structseq.py Lib/test/test_subprocess.py Lib/test/test_sunau.py Lib/test/test_sundry.py Lib/test/test_sys.py Lib/test/test_sys_setprofile.py Lib/test/test_sys_settrace.py Lib/test/test_sysconfig.py Lib/test/test_tarfile.py Lib/test/test_tcl.py Lib/test/test_threaded_import.py Lib/test/test_threading.py Lib/test/test_threading_local.py Lib/test/test_tokenize.py Lib/test/test_trace.py Lib/test/test_unicode.py Lib/test/test_unicodedata.py Lib/test/test_unpack.py Lib/test/test_urllib.py Lib/test/test_urllib2.py Lib/test/test_urlparse.py Lib/test/test_xml_etree.py Lib/threading.py Lib/timeit.py Lib/tkinter/__main__.py Lib/trace.py Lib/unittest/case.py Lib/unittest/test/test_case.py Lib/urllib/parse.py Lib/urllib/request.py Lib/urllib/robotparser.py Lib/xml/dom/expatbuilder.py Lib/xml/dom/minidom.py Lib/xmlrpc/client.py Makefile.pre.in Misc/ACKS Misc/NEWS Misc/RPM/python-3.2.spec Misc/developers.txt Misc/maintainers.rst Misc/python-wing.wpr Modules/Setup.dist Modules/_ctypes/_ctypes.c Modules/_ctypes/callproc.c Modules/_ctypes/cfield.c Modules/_ctypes/ctypes.h Modules/_cursesmodule.c Modules/_datetimemodule.c Modules/_io/_iomodule.c Modules/_io/bufferedio.c Modules/_json.c Modules/_pickle.c Modules/_ssl.c Modules/_struct.c Modules/_time.c Modules/_time.h Modules/arraymodule.c Modules/binascii.c Modules/bz2module.c Modules/cmathmodule.c Modules/datetimemodule.c Modules/getpath.c Modules/main.c Modules/makesetup Modules/mathmodule.c Modules/mmapmodule.c Modules/posixmodule.c Modules/readline.c Modules/selectmodule.c Modules/sha1module.c Modules/signalmodule.c Modules/timemodule.c Modules/zipimport.c Modules/zlib/ChangeLog Modules/zlib/FAQ Modules/zlib/INDEX Modules/zlib/Makefile Modules/zlib/Makefile.in Modules/zlib/README Modules/zlib/adler32.c Modules/zlib/compress.c Modules/zlib/configure Modules/zlib/crc32.c Modules/zlib/deflate.c Modules/zlib/deflate.h Modules/zlib/example.c Modules/zlib/infback.c Modules/zlib/inffast.c Modules/zlib/inffast.h Modules/zlib/inflate.c Modules/zlib/inflate.h Modules/zlib/inftrees.c Modules/zlib/inftrees.h Modules/zlib/make_vms.com Modules/zlib/minigzip.c Modules/zlib/trees.c Modules/zlib/trees.h Modules/zlib/uncompr.c Modules/zlib/zconf.h Modules/zlib/zlib.3 Modules/zlib/zlib.h Modules/zlib/zutil.c Modules/zlib/zutil.h Objects/bytearrayobject.c Objects/complexobject.c Objects/floatobject.c Objects/iterobject.c Objects/memoryobject.c Objects/object.c Objects/setobject.c Objects/sliceobject.c Objects/stringlib/formatter.h Objects/structseq.c Objects/unicodeobject.c PC/VS8.0/pythoncore.vcproj PC/_subprocess.c PC/config.c PC/python_nt.rc PCbuild/build_ssl.py PCbuild/pyproject.vsprops PCbuild/pythoncore.vcproj PCbuild/readme.txt Python/ceval.c Python/getargs.c Python/getcopyright.c Python/getversion.c Python/graminit.c Python/pythonrun.c Python/pytime.c README Tools/README Tools/buildbot/external-common.bat Tools/faqwiz Tools/framer/framer/bases.py Tools/framer/framer/template.py Tools/freeze/freeze.py Tools/freeze/makeconfig.py Tools/gdb/libpython.py Tools/i18n/msgfmt.py Tools/msi/msi.py Tools/msi/msilib.py Tools/msi/uuids.py Tools/pynche/ChipViewer.py Tools/pynche/DetailsViewer.py Tools/pynche/ListViewer.py Tools/pynche/PyncheWidget.py Tools/pynche/StripViewer.py Tools/pynche/Switchboard.py Tools/pynche/TextViewer.py Tools/pynche/TypeinViewer.py Tools/scripts/README Tools/scripts/byext.py Tools/scripts/checkappend.py Tools/scripts/classfix.py Tools/scripts/cvsfiles.py Tools/scripts/logmerge.py Tools/scripts/methfix.py Tools/scripts/serve.py Tools/scripts/setup.py Tools/scripts/win_add2path.py Tools/scripts/xxci.py Tools/versioncheck configure configure.in setup.py Message-ID: <20100808104131.5253BEEA79@mail.python.org> Author: mark.dickinson Date: Sun Aug 8 12:41:24 2010 New Revision: 83814 Log: Merged revisions 82615,82617,82624-82626,82628-82630,82632-82633,82636-82637,82642,82644,82646-82647,82649-82650,82654,82659,82661-82663,82723-82724,82730-82731,82735,82737,82739,82741,82743-82746,82748-82750,82753,82757-82764,82766-82767,82771,82777,82780,82784-82785,82789-82790,82796,82798-82799,82801,82804-82806,82814,82817-82822,82825,82828,82830-82832,82834-82835,82837-82839,82842-82843,82848-82850,82856-82857,82871-82874,82879,82881,82885,82887,82890,82895,82899,82910,82915,82918-82919,82921-82922,82927,82931,82934,82937,82941,82943,82947,82949,82951-82952,82954,82957-82962,82964-82967,82971,82978,82983,82985,82995,82997,83005-83006,83009,83011,83016,83025,83030,83035,83046,83053,83059,83065-83067,83072-83073,83075,83078,83080,83088-83092,83094-83096,83099-83103,83106-83109,83111-83112,83115-83116,83120,83125,83127,83133,83139-83141,83149-83152,83154,83156-83157,83160-83163,83166,83168-83171,83173,83177,83181-83184,83186,83188,83191,83195-83197,83201-83202,83209,83212-83220,83222-83224,83226-83227,83229-83232,83234-83239,83259-83266,83268-83272,83274-83275,83277,83281,83283-83289,83291-83294,83296,83307-83308,83313,83315-83329,83335-83339,83341-83343,83348-83353,83355-83362,83366,83368-83376,83380,83384-83391,83393-83400,83403-83408,83411,83415,83417,83431,83440,83444,83456,83479,83488,83494,83501,83506,83513,83516,83521,83523-83529,83531,83536,83538,83542-83543,83546-83548,83550-83555,83558,83560-83561,83563,83565-83566,83569,83571,83574-83575,83580,83584,83599,83605-83618,83636,83644,83649,83659-83660,83663,83667,83670,83673,83675,83677-83678,83681,83683,83690,83696,83698-83699,83701,83705,83707-83709,83712-83715,83719,83722,83726-83727,83729,83731-83732,83735-83736,83741-83742,83744-83745,83747,83750-83752,83755,83758-83759,83762-83763,83767-83768,83770-83771,83774-83780,83783-83785,83792,83795,83802,83804,83808-83809 via svnmerge from svn+ssh://pythondev at svn.python.org/python/branches/py3k ........ r82615 | georg.brandl | 2010-07-06 23:58:50 +0100 (Tue, 06 Jul 2010) | 1 line Fix typo. ........ r82617 | alexander.belopolsky | 2010-07-07 00:19:45 +0100 (Wed, 07 Jul 2010) | 1 line Issue #9000: datetime.timezone objects now have eval-friendly repr. ........ r82624 | mark.dickinson | 2010-07-07 17:10:44 +0100 (Wed, 07 Jul 2010) | 1 line Kill gratuitous space. ........ r82625 | mark.dickinson | 2010-07-07 17:17:31 +0100 (Wed, 07 Jul 2010) | 1 line Minor refactoring in lgamma code, for clarity. ........ r82626 | mark.dickinson | 2010-07-07 17:21:29 +0100 (Wed, 07 Jul 2010) | 1 line Issue #9186: log1p(-1.0) should raise ValueError, not OverflowError. ........ r82628 | benjamin.peterson | 2010-07-07 19:44:05 +0100 (Wed, 07 Jul 2010) | 1 line this needn't be in the loop ........ r82629 | georg.brandl | 2010-07-07 19:51:43 +0100 (Wed, 07 Jul 2010) | 1 line Make comment out of an awkward note. ........ r82630 | benjamin.peterson | 2010-07-07 19:54:59 +0100 (Wed, 07 Jul 2010) | 1 line don't ignore exceptions from PyObject_IsTrue ........ r82632 | georg.brandl | 2010-07-07 20:04:36 +0100 (Wed, 07 Jul 2010) | 1 line Turn more notes into comments. ........ r82633 | benjamin.peterson | 2010-07-07 20:04:48 +0100 (Wed, 07 Jul 2010) | 1 line add NEWS ........ r82636 | benjamin.peterson | 2010-07-07 21:54:01 +0100 (Wed, 07 Jul 2010) | 4 lines make struct sequences subclass tuple; kill lots of code This fixes #8413. ........ r82637 | benjamin.peterson | 2010-07-07 23:45:06 +0100 (Wed, 07 Jul 2010) | 1 line ValueError in this case is also acceptable ........ r82642 | alexander.belopolsky | 2010-07-08 00:56:38 +0100 (Thu, 08 Jul 2010) | 1 line Issue #5288: Eliminated round-trips between timdelta and int offsets ........ r82644 | ezio.melotti | 2010-07-08 16:03:02 +0100 (Thu, 08 Jul 2010) | 1 line Fix typo in a comment in mathmodule.c. ........ r82646 | mark.dickinson | 2010-07-08 18:23:40 +0100 (Thu, 08 Jul 2010) | 1 line In test_decimal, convert heuristic for skipping tests into an explicit skiplist. ........ r82647 | antoine.pitrou | 2010-07-08 19:51:30 +0100 (Thu, 08 Jul 2010) | 3 lines Issue #8605: Skip test_gdb if Python is compiled with optimizations. ........ r82649 | mark.dickinson | 2010-07-08 20:03:34 +0100 (Thu, 08 Jul 2010) | 1 line Fix a performance issue in Decimal.pow. Thanks Stefan Krah for finding this. ........ r82650 | mark.dickinson | 2010-07-08 20:09:16 +0100 (Thu, 08 Jul 2010) | 1 line Fix misplaced exactness check that was causing unnecessary work in Decimal.__pow__. ........ r82654 | mark.dickinson | 2010-07-08 22:15:36 +0100 (Thu, 08 Jul 2010) | 3 lines Issue #9136: Profiling Decimal gave 'dictionary changed size during iteration'. Remove the use of locals() that caused this error. ........ r82659 | brian.curtin | 2010-07-08 22:39:08 +0100 (Thu, 08 Jul 2010) | 17 lines Implement #1578269. Patch by Jason R. Coombs. Added Windows support for os.symlink when run on Windows 6.0 or greater, aka Vista. Previous Windows versions will raise NotImplementedError when trying to symlink. Includes numerous test updates and additions to test_os, including a symlink_support module because of the fact that privilege escalation is required in order to run the tests to ensure that the user is able to create symlinks. By default, accounts do not have the required privilege, so the escalation code will have to be exposed later (or documented on how to do so). I'll be following up with that work next. Note that the tests use ctypes, which was agreed on during the PyCon language summit. ........ r82661 | brian.curtin | 2010-07-08 23:13:25 +0100 (Thu, 08 Jul 2010) | 2 lines Undo inadvertant checkin. ........ r82662 | benjamin.peterson | 2010-07-08 23:16:05 +0100 (Thu, 08 Jul 2010) | 1 line assert tuple inheritance ........ r82663 | benjamin.peterson | 2010-07-08 23:33:03 +0100 (Thu, 08 Jul 2010) | 1 line fix repr of complicated structseqs #9206 ........ r82723 | benjamin.peterson | 2010-07-09 02:58:26 +0100 (Fri, 09 Jul 2010) | 1 line use assert method ........ r82724 | georg.brandl | 2010-07-09 08:33:15 +0100 (Fri, 09 Jul 2010) | 1 line 2.7 is now stable. ........ r82730 | r.david.murray | 2010-07-09 13:23:21 +0100 (Fri, 09 Jul 2010) | 4 lines 7846: limit fnmatch pattern cache to _MAXCACHE=100 entries. Patch by Andrew Clegg. ........ r82731 | r.david.murray | 2010-07-09 14:14:03 +0100 (Fri, 09 Jul 2010) | 2 lines Fix sort order mistake in Misc/ACKS. ........ r82735 | benjamin.peterson | 2010-07-09 14:20:40 +0100 (Fri, 09 Jul 2010) | 1 line OverflowError is fine ........ r82737 | brian.curtin | 2010-07-09 14:22:07 +0100 (Fri, 09 Jul 2010) | 2 lines Reorder Jason "two O's" Coombs ........ r82739 | benjamin.peterson | 2010-07-09 14:28:42 +0100 (Fri, 09 Jul 2010) | 1 line allow more exceptions ........ r82741 | benjamin.peterson | 2010-07-09 14:31:11 +0100 (Fri, 09 Jul 2010) | 1 line wrap ........ r82743 | brian.curtin | 2010-07-09 14:54:27 +0100 (Fri, 09 Jul 2010) | 8 lines Fix the breakage of Lib/tarfile.py on non-Windows platforms due to using WindowsError in a try/except. Only add WindowsError to the list of exceptions to catch when we are actually running on Windows. Additionally, add a call that was left out in test_posixpath. Thanks Amaury, Antoine, and Jason. ........ r82744 | brian.curtin | 2010-07-09 16:15:09 +0100 (Fri, 09 Jul 2010) | 3 lines Adding versionchanged to the various Windows os.symlink additions, along with a few minor touchups. ........ r82745 | brian.curtin | 2010-07-09 16:58:59 +0100 (Fri, 09 Jul 2010) | 2 lines Re-flow several long lines from #1578269. ........ r82746 | jeffrey.yasskin | 2010-07-09 17:30:58 +0100 (Fri, 09 Jul 2010) | 12 lines Issue #9189: Allow users to set $CFLAGS, $CPPFLAGS, and $LDFLAGS when running configure to append to Python's default values for those variables, and similarly allow users to set $XXFLAGS on the make command line to append to the values set by configure. In the makefile, this renames the variables that used to be $XXFLAGS to $PY_XXFLAGS, and renames the old $PY_CFLAGS to $PY_CORE_CFLAGS. To compensate, sysconfig now aliases $XXFLAGS=$PY_XXFLAGS so that scripts using it keep working. I see that as the right interface, not a backward-compatibility hack, since these are logically the $XXFLAGS variables; we just use a different name in the makefile to deal with make's semantics. ........ r82748 | benjamin.peterson | 2010-07-09 19:28:25 +0100 (Fri, 09 Jul 2010) | 1 line remove SocketIO.__del__; close() can handle it ........ r82749 | mark.dickinson | 2010-07-09 20:25:48 +0100 (Fri, 09 Jul 2010) | 1 line Silence gcc warning. (In function 'bytearray_init': warning: 'value' may be used uninitialized in this function). ........ r82750 | benjamin.peterson | 2010-07-09 20:37:00 +0100 (Fri, 09 Jul 2010) | 1 line be more robust across platforms ........ r82753 | jeffrey.yasskin | 2010-07-09 20:55:05 +0100 (Fri, 09 Jul 2010) | 2 lines Oops. Fix distutils tests that r82746 broke. ........ r82757 | georg.brandl | 2010-07-10 09:58:37 +0100 (Sat, 10 Jul 2010) | 1 line Fix markup. ........ r82758 | georg.brandl | 2010-07-10 11:23:40 +0100 (Sat, 10 Jul 2010) | 1 line Emphasize role of count for Pascal string. ........ r82759 | georg.brandl | 2010-07-10 11:32:36 +0100 (Sat, 10 Jul 2010) | 1 line #3071: tell how many values were expected when unpacking too many. ........ r82760 | georg.brandl | 2010-07-10 11:39:57 +0100 (Sat, 10 Jul 2010) | 1 line #3214: improve description of duck-typing in glossary. ........ r82761 | georg.brandl | 2010-07-10 12:40:13 +0100 (Sat, 10 Jul 2010) | 1 line #1434090: properly append child in expatbuilder doctype handler. ........ r82762 | georg.brandl | 2010-07-10 12:51:06 +0100 (Sat, 10 Jul 2010) | 1 line #8338: fix outdated class name. ........ r82763 | georg.brandl | 2010-07-10 13:01:34 +0100 (Sat, 10 Jul 2010) | 1 line #8456: fix signature of sqlite3.connect(). ........ r82764 | georg.brandl | 2010-07-10 13:20:38 +0100 (Sat, 10 Jul 2010) | 1 line #8564: update docs on integrating doctest/unittest with unittest(2) test discovery. ........ r82766 | r.david.murray | 2010-07-10 14:52:13 +0100 (Sat, 10 Jul 2010) | 5 lines Fix 'refleak' introduced by fnmatch cache purge tests. This introduces a 'purge' function for the fnmatch module analogous to the 'purge' function in the re module. ........ r82767 | michael.foord | 2010-07-10 14:52:22 +0100 (Sat, 10 Jul 2010) | 2 lines Fix error message for comparing single line strings in unittest.TestCase.assertEqual. Issue 9174. ........ r82771 | r.david.murray | 2010-07-10 15:23:36 +0100 (Sat, 10 Jul 2010) | 2 lines Add missing docs for re module 'purge' function. ........ r82777 | benjamin.peterson | 2010-07-10 16:14:45 +0100 (Sat, 10 Jul 2010) | 1 line ValueError is eventually what we want to move to, I suppose ........ r82780 | senthil.kumaran | 2010-07-11 04:12:43 +0100 (Sun, 11 Jul 2010) | 3 lines Stricter verification for file based url scheme and reliance on ftp protocol. ........ r82784 | benjamin.peterson | 2010-07-11 04:36:35 +0100 (Sun, 11 Jul 2010) | 1 line remove unneeded error check ........ r82785 | senthil.kumaran | 2010-07-11 06:01:52 +0100 (Sun, 11 Jul 2010) | 3 lines Issue2007: Removing reference to MSIE Cookie handling by mechanize. Suggested by John J Lee. ........ r82789 | georg.brandl | 2010-07-11 09:33:16 +0100 (Sun, 11 Jul 2010) | 1 line Silence makeindex. ........ r82790 | georg.brandl | 2010-07-11 09:36:20 +0100 (Sun, 11 Jul 2010) | 1 line #3214 followup: add link to ABC entry. ........ r82796 | ronald.oussoren | 2010-07-11 10:05:07 +0100 (Sun, 11 Jul 2010) | 2 lines Fix for issue 9164 ........ r82798 | georg.brandl | 2010-07-11 10:23:11 +0100 (Sun, 11 Jul 2010) | 1 line #6774: explain shutdown() behavior varying with platform. ........ r82799 | georg.brandl | 2010-07-11 10:26:57 +0100 (Sun, 11 Jul 2010) | 1 line Fix typo. ........ r82801 | georg.brandl | 2010-07-11 10:33:39 +0100 (Sun, 11 Jul 2010) | 1 line #9184: fix default value for "buffering" param of open(). ........ r82804 | georg.brandl | 2010-07-11 10:41:21 +0100 (Sun, 11 Jul 2010) | 1 line Allow set literals in literal_eval(). ........ r82805 | georg.brandl | 2010-07-11 10:42:10 +0100 (Sun, 11 Jul 2010) | 1 line #7935: cross-reference to ast.literal_eval() from eval() docs. ........ r82806 | georg.brandl | 2010-07-11 11:22:44 +0100 (Sun, 11 Jul 2010) | 1 line #9223: link to Command class reference, and move Command interface docs nearer to class docs. ........ r82814 | antoine.pitrou | 2010-07-11 13:12:00 +0100 (Sun, 11 Jul 2010) | 4 lines Issue #7616: Fix copying of overlapping memoryview slices with the Intel compiler. ........ r82817 | benjamin.peterson | 2010-07-11 13:59:24 +0100 (Sun, 11 Jul 2010) | 1 line test for set literals ........ r82818 | mark.dickinson | 2010-07-11 18:38:24 +0100 (Sun, 11 Jul 2010) | 2 lines Issue #9165: Add math.isfinite and cmath.isfinite. ........ r82819 | martin.v.loewis | 2010-07-11 18:39:46 +0100 (Sun, 11 Jul 2010) | 2 lines Issue #8974: fix print calls in msgfmt.py. ........ r82820 | mark.dickinson | 2010-07-11 19:13:41 +0100 (Sun, 11 Jul 2010) | 1 line Improve docstrings for isnan, isinf and isfinite. ........ r82821 | mark.dickinson | 2010-07-11 19:53:06 +0100 (Sun, 11 Jul 2010) | 3 lines Issue #9137: Fix issue in MutableMapping.update, which incorrectly treated keyword arguments called 'self' or 'other' specially. ........ r82822 | mark.dickinson | 2010-07-11 20:12:10 +0100 (Sun, 11 Jul 2010) | 1 line assertEquals -> assertEqual ........ r82825 | mark.dickinson | 2010-07-11 20:47:37 +0100 (Sun, 11 Jul 2010) | 1 line Include versionadded in (c)math.isfinite docs (thanks Ezio Melotti). Other minor doc cleanups. ........ r82828 | benjamin.peterson | 2010-07-12 00:06:06 +0100 (Mon, 12 Jul 2010) | 1 line allow byte literals ........ r82830 | brian.curtin | 2010-07-12 00:32:11 +0100 (Mon, 12 Jul 2010) | 2 lines Put my name down for winreg. ........ r82831 | martin.v.loewis | 2010-07-12 07:03:18 +0100 (Mon, 12 Jul 2010) | 2 lines Remove myself. ........ r82832 | georg.brandl | 2010-07-12 10:00:29 +0100 (Mon, 12 Jul 2010) | 1 line Take care of duplicate target name warnings. ........ r82834 | georg.brandl | 2010-07-12 10:06:13 +0100 (Mon, 12 Jul 2010) | 1 line Use raw string literals for regexes containing backlash. ........ r82835 | mark.dickinson | 2010-07-12 10:37:40 +0100 (Mon, 12 Jul 2010) | 1 line Remove mention of execfile from the tutorial. ........ r82837 | mark.dickinson | 2010-07-12 15:14:18 +0100 (Mon, 12 Jul 2010) | 1 line Issue #2009: refactor varargslist and typedargslist productions to make them more friendly for third-party parsers. ........ r82838 | mark.dickinson | 2010-07-12 15:18:21 +0100 (Mon, 12 Jul 2010) | 1 line Regenerate Python/graminit.c. ........ r82839 | ezio.melotti | 2010-07-12 20:49:41 +0100 (Mon, 12 Jul 2010) | 1 line #6026: skip test_get_file_list when zlib is not available. ........ r82842 | antoine.pitrou | 2010-07-12 21:01:52 +0100 (Mon, 12 Jul 2010) | 3 lines Fix definition of len() and indexing for memoryview objects (part of #7696). ........ r82843 | mark.dickinson | 2010-07-12 21:03:24 +0100 (Mon, 12 Jul 2010) | 1 line Yield more information on failure in test_struct boolean test. ........ r82848 | georg.brandl | 2010-07-13 07:38:10 +0100 (Tue, 13 Jul 2010) | 1 line Add bytes in literal_eval doc. ........ r82849 | jeroen.ruigrok | 2010-07-13 15:47:01 +0100 (Tue, 13 Jul 2010) | 2 lines Fix documentation typo: wprite() -> write(). ........ r82850 | alexander.belopolsky | 2010-07-13 15:50:16 +0100 (Tue, 13 Jul 2010) | 1 line Set sys.modules[name] to None instead of 0 to block module import. ........ r82856 | victor.stinner | 2010-07-14 00:04:56 +0100 (Wed, 14 Jul 2010) | 2 lines Issue #9243: Fix sndhdr module and add unit tests, contributed by James Lee. ........ r82857 | victor.stinner | 2010-07-14 00:08:01 +0100 (Wed, 14 Jul 2010) | 2 lines Woops, test_sndhdr.py contains the same code twice: fix it ........ r82871 | georg.brandl | 2010-07-14 09:00:22 +0100 (Wed, 14 Jul 2010) | 1 line #9258: fix copy-paste errors. ........ r82872 | georg.brandl | 2010-07-14 09:53:18 +0100 (Wed, 14 Jul 2010) | 1 line Remove XXX from text. ........ r82873 | georg.brandl | 2010-07-14 09:53:36 +0100 (Wed, 14 Jul 2010) | 1 line Remove unused code that would raise a NameError. ........ r82874 | georg.brandl | 2010-07-14 09:54:40 +0100 (Wed, 14 Jul 2010) | 1 line #9235: fix missing import of sys. ........ r82879 | stefan.krah | 2010-07-14 11:16:11 +0100 (Wed, 14 Jul 2010) | 3 lines High byte is the exit status. ........ r82881 | senthil.kumaran | 2010-07-14 11:21:22 +0100 (Wed, 14 Jul 2010) | 3 lines Fix Issue5842 - Moving the tests out of urllib.parse module ........ r82885 | antoine.pitrou | 2010-07-14 12:52:38 +0100 (Wed, 14 Jul 2010) | 4 lines Issue #9251: test_threaded_import didn't fail when run through regrtest if the import lock was disabled. ........ r82887 | alexander.belopolsky | 2010-07-14 14:46:57 +0100 (Wed, 14 Jul 2010) | 1 line PEP 8 conformance: class_ -> cls ........ r82890 | senthil.kumaran | 2010-07-14 20:15:23 +0100 (Wed, 14 Jul 2010) | 3 lines Fix: Issue6853 - Get HTTPS system proxy in Windows. ........ r82895 | senthil.kumaran | 2010-07-14 21:10:52 +0100 (Wed, 14 Jul 2010) | 3 lines Fix a mistake, https proxy shoud be https:// ........ r82899 | senthil.kumaran | 2010-07-14 21:30:02 +0100 (Wed, 14 Jul 2010) | 2 lines Fix issue9132 - Documentation for comparing dictionaries is out of date ........ r82910 | brett.cannon | 2010-07-15 07:24:04 +0100 (Thu, 15 Jul 2010) | 2 lines Touch up comments and code along with outputting what the unit of measure is. ........ r82915 | alexander.belopolsky | 2010-07-16 15:39:45 +0100 (Fri, 16 Jul 2010) | 1 line Corrected TUPLE opcodes' docs. ........ r82918 | brett.cannon | 2010-07-16 20:04:29 +0100 (Fri, 16 Jul 2010) | 4 lines Add benchmarks for importing just source w/o writing bytecode, importing source while writing bytecode, and importing bytecode with source existing (don't care about sourceless imports). ........ r82919 | antoine.pitrou | 2010-07-16 20:10:38 +0100 (Fri, 16 Jul 2010) | 4 lines Fix possible failure in pickling tests due to different instantiations of the random module being around. ........ r82921 | brett.cannon | 2010-07-16 20:26:23 +0100 (Fri, 16 Jul 2010) | 1 line Add comma grouping to max result so it's easier to read. ........ r82922 | r.david.murray | 2010-07-17 02:19:57 +0100 (Sat, 17 Jul 2010) | 4 lines #1555570: correctly handle a \r\n that is split by the read buffer. Patch and test by Tony Nelson. ........ r82927 | stefan.krah | 2010-07-17 12:46:52 +0100 (Sat, 17 Jul 2010) | 4 lines Issue #7384: On Gentoo, libreadline.so is a "fake library", so ldd fails. In that case, do not attempt to parse stderr output. ........ r82931 | alexander.belopolsky | 2010-07-17 16:51:21 +0100 (Sat, 17 Jul 2010) | 2 lines Issue #9268: Add annotation option to pickletools.dis ........ r82934 | benjamin.peterson | 2010-07-17 21:39:23 +0100 (Sat, 17 Jul 2010) | 1 line sharedinstall should depend on sharedmods #9280 ........ r82937 | alexander.belopolsky | 2010-07-17 23:50:45 +0100 (Sat, 17 Jul 2010) | 3 lines Issue #5180: Fixed a bug that prevented loading 2.x pickles in 3.x python when they contain instances of old-style classes. ........ r82941 | mark.dickinson | 2010-07-18 08:29:02 +0100 (Sun, 18 Jul 2010) | 3 lines Issue #9277: Struct module: standard bool packing was incorrect if char is unsigned. Thanks Stefan Krah for the patch. ........ r82943 | mark.dickinson | 2010-07-18 08:48:20 +0100 (Sun, 18 Jul 2010) | 1 line Misc/NEWS entry for r82941. ........ r82947 | mark.dickinson | 2010-07-18 09:03:10 +0100 (Sun, 18 Jul 2010) | 1 line Clarify Misc/NEWS entry. ........ r82949 | georg.brandl | 2010-07-18 11:11:03 +0100 (Sun, 18 Jul 2010) | 1 line #9279: remove the pdb.doc file, put its contents in pdb.__doc__. Also sync this and the pdb docs, introduce a new directive for pdb commands and a role to link to them. ........ r82951 | georg.brandl | 2010-07-18 14:43:32 +0100 (Sun, 18 Jul 2010) | 1 line #9110: update to ContextDecorator doc. ........ r82952 | benjamin.peterson | 2010-07-18 15:23:36 +0100 (Sun, 18 Jul 2010) | 1 line use classmethod ........ r82954 | benjamin.peterson | 2010-07-18 15:27:02 +0100 (Sun, 18 Jul 2010) | 1 line nest method and attribute doc ........ r82957 | mark.dickinson | 2010-07-18 17:01:36 +0100 (Sun, 18 Jul 2010) | 1 line Add configure check for a bug with gcc soft floating-point and subnormals, to help debug issue 8265. ........ r82958 | jean-paul.calderone | 2010-07-18 17:13:27 +0100 (Sun, 18 Jul 2010) | 1 line There is no method named "register(fd, eventmask)"; fix markup to just indicate this is code. ........ r82959 | jean-paul.calderone | 2010-07-18 17:30:31 +0100 (Sun, 18 Jul 2010) | 1 line Document the extra epoll flags ........ r82960 | georg.brandl | 2010-07-19 07:52:35 +0100 (Mon, 19 Jul 2010) | 1 line Clarify. ........ r82961 | georg.brandl | 2010-07-19 07:57:52 +0100 (Mon, 19 Jul 2010) | 1 line Clarify :option: description. ........ r82962 | mark.dickinson | 2010-07-19 08:31:40 +0100 (Mon, 19 Jul 2010) | 1 line Remove temporary debugging code. ........ r82964 | georg.brandl | 2010-07-19 09:02:46 +0100 (Mon, 19 Jul 2010) | 1 line pydoc.pager does not promise to use $PAGER. ........ r82965 | georg.brandl | 2010-07-19 12:28:05 +0100 (Mon, 19 Jul 2010) | 1 line Clarification. Yay importlib! ........ r82966 | stefan.krah | 2010-07-19 13:36:57 +0100 (Mon, 19 Jul 2010) | 13 lines Issue #9036: Throughout the code base, Py_CHARMASK is used on 8-bit wide signed/unsigned chars or on integers directly derived from those. In all cases, it could be replaced by a simple cast to (unsigned char). Reasons for the change: a) Make the comment more explicit. b) If char is unsigned, the cast is optimized away. c) If char is unsigned, gcc emits spurious "array subscript has type 'char'" warnings. ........ r82967 | ronald.oussoren | 2010-07-19 14:00:36 +0100 (Mon, 19 Jul 2010) | 8 lines This patch adds a testcase that demonstrates a problem with the expansion of LDSHARED when accessing that value through sysconfig. The problem is probably caused by the 9189. A fix will follow shortly. ........ r82971 | stefan.krah | 2010-07-19 15:20:53 +0100 (Mon, 19 Jul 2010) | 4 lines Issue #9265: Incorrect name passed as arg[0] when shell=True and executable specified. ........ r82978 | stefan.krah | 2010-07-19 18:58:26 +0100 (Mon, 19 Jul 2010) | 3 lines Sub-issue of #9036: Fix incorrect use of Py_CHARMASK. ........ r82983 | senthil.kumaran | 2010-07-19 19:17:19 +0100 (Mon, 19 Jul 2010) | 3 lines Fix Issue9301 - urllib.parse.unquote and unquote_to_byte to raise TypeError for None. ........ r82985 | gregory.p.smith | 2010-07-20 00:17:22 +0100 (Tue, 20 Jul 2010) | 3 lines Fixes Issue #3704: http.cookiejar was not properly handling URLs with a / in the parameters. (This is jjlee's issue3704.patch ported to py3k) ........ r82995 | ronald.oussoren | 2010-07-20 17:07:10 +0100 (Tue, 20 Jul 2010) | 14 lines Without this patch the value of sysconfig.get_config_var('LDSHARED') is wrong when PY_LDFLAGS is not empty. The bug was caused by LDSHARED getting expanded *before* sysconfig renamed PY_LDSHARED (and simular values) to names without a PY_ prefix. The patch tries to maintain the intended behaviour of allowing users to set LDFLAGS in the environment and have that affect the build. Without this patch a universal build on OSX cannot build universal (fat binary) extensions. ........ r82997 | alexander.belopolsky | 2010-07-20 20:55:18 +0100 (Tue, 20 Jul 2010) | 3 lines Issue #9282: Fixed --listfuncs option of trace.py. Thanks Eli Bendersky for the patch. ........ r83005 | benjamin.peterson | 2010-07-20 23:37:19 +0100 (Tue, 20 Jul 2010) | 1 line move test_trace.py so as not to conflict with future tests for the trace module ........ r83006 | benjamin.peterson | 2010-07-20 23:39:34 +0100 (Tue, 20 Jul 2010) | 1 line revert unintended changes ........ r83009 | brian.curtin | 2010-07-21 02:44:19 +0100 (Wed, 21 Jul 2010) | 2 lines Fix #9316. if/is grammar corrections. ........ r83011 | brett.cannon | 2010-07-21 10:48:31 +0100 (Wed, 21 Jul 2010) | 2 lines Minor clarification about importlib.abc.SourceLoader.get_filename. ........ r83016 | doug.hellmann | 2010-07-21 13:29:04 +0100 (Wed, 21 Jul 2010) | 1 line Apply patch from Ray Allen for issue 9296 ........ r83025 | antoine.pitrou | 2010-07-21 16:54:48 +0100 (Wed, 21 Jul 2010) | 3 lines Remove outdated mention of deprecated functions in the string module - they have been removed in 3.x. ........ r83030 | antoine.pitrou | 2010-07-21 17:41:31 +0100 (Wed, 21 Jul 2010) | 5 lines Issue #5395: check that array.fromfile() re-raises an IOError instead of replacing it with EOFError. (this is only an added test, but 2.x will get a fix too) ........ r83035 | alexander.belopolsky | 2010-07-21 18:43:42 +0100 (Wed, 21 Jul 2010) | 3 lines Issue #9323: Fixed a bug in trace.py that resulted in loosing the name of the script being traced. Patch by Eli Bendersky. ........ r83046 | brett.cannon | 2010-07-22 08:40:56 +0100 (Thu, 22 Jul 2010) | 3 lines Add importlib benchmarks which try to be "realistic" by importing the decimal module which is the largest module in the stdlib. ........ r83053 | tarek.ziade | 2010-07-22 13:50:05 +0100 (Thu, 22 Jul 2010) | 1 line reverted distutils its 3.1 state. All new work is now happening in disutils2, and distutils is now feature-frozen. ........ r83059 | brian.curtin | 2010-07-22 16:38:28 +0100 (Thu, 22 Jul 2010) | 3 lines Skip this test as it doesn't apply to Windows. It was added for #9189 for some GCC flags. ........ r83065 | georg.brandl | 2010-07-23 09:46:35 +0100 (Fri, 23 Jul 2010) | 1 line Use augassign. ........ r83066 | ronald.oussoren | 2010-07-23 10:43:17 +0100 (Fri, 23 Jul 2010) | 10 lines Ensure that the Makefile variable expansion in distutils.sysconfig matches that in the toplevel sysconfig module. Without this patch universal builds on OSX are broken. Als add a test that checks that the two version of get_config_vars agree on important values. ........ r83067 | ronald.oussoren | 2010-07-23 10:50:05 +0100 (Fri, 23 Jul 2010) | 8 lines Workaround for issue 4047: in some configurations of the Crash Reporter on OSX test_subprocess will trigger the reporter. This patch prints a warning when the Crash Reporter will get triggered intentionally, which should avoid confusing people. ........ r83072 | brett.cannon | 2010-07-23 12:31:31 +0100 (Fri, 23 Jul 2010) | 5 lines Document the fact that the 'test' package is meant only for use by Python itself and not by others. Closes issue 9255. ........ r83073 | ronald.oussoren | 2010-07-23 12:48:36 +0100 (Fri, 23 Jul 2010) | 4 lines Minor tweak of test_osx_env to avoid failing when the framework is not yet installed. ........ r83075 | ronald.oussoren | 2010-07-23 12:54:59 +0100 (Fri, 23 Jul 2010) | 5 lines Fix for issue 7895. Avoid crashing the interpreter when calling platform.mac_ver after calling os.fork by reading from a system configuration file instead of using OSX APIs. ........ r83078 | martin.v.loewis | 2010-07-23 13:16:41 +0100 (Fri, 23 Jul 2010) | 3 lines Issue #6095: Make directory argument to os.listdir optional. Patch by Virgil Dupras. ........ r83080 | brett.cannon | 2010-07-23 13:26:35 +0100 (Fri, 23 Jul 2010) | 5 lines Clarify the wording for threading.is_alive() to not suggest something is "roughly" done. Closes issue 9339. Thanks Brian Brazil for the patch. ........ r83088 | ronald.oussoren | 2010-07-23 14:53:51 +0100 (Fri, 23 Jul 2010) | 8 lines This fixes issue7900 by adding code that deals with the fact that getgroups(2) might return more that MAX_GROUPS on OSX. See the issue (and python-dev archives) for the gory details. Summarized: OSX behaves rather oddly and Apple says this is intentional. ........ r83089 | brett.cannon | 2010-07-23 14:54:14 +0100 (Fri, 23 Jul 2010) | 4 lines Test calendar.monthrange. Closes issue 9342. Thanks John Chandler for the patch. ........ r83090 | brett.cannon | 2010-07-23 15:03:16 +0100 (Fri, 23 Jul 2010) | 4 lines Explicitly test relative imports by reusing importlib tests. Closes issue 8392. Thanks Virgil Dupras for the initial patch. ........ r83091 | brett.cannon | 2010-07-23 15:45:19 +0100 (Fri, 23 Jul 2010) | 1 line Stop shadowing a test class. ........ r83092 | brett.cannon | 2010-07-23 16:43:14 +0100 (Fri, 23 Jul 2010) | 2 lines Rename some macros in the sha1 module to no longer conflict with termios.h. ........ r83094 | brett.cannon | 2010-07-23 16:50:02 +0100 (Fri, 23 Jul 2010) | 4 lines Rip out old testing code that was inlined in threading. Partially closes issue 9346. Thanks Brian Brazil for the patch. ........ r83095 | brett.cannon | 2010-07-23 16:50:52 +0100 (Fri, 23 Jul 2010) | 4 lines Add more tests for the threading.Thread.repr. Partially closes issue 9346. Thanks to Brian Brazil for the patch. ........ r83096 | ronald.oussoren | 2010-07-23 17:05:35 +0100 (Fri, 23 Jul 2010) | 13 lines Ensure that sys.prefix can reliably be found on OSX. This fixes a small issue that was exposed by running test_subprocess through regrtest (and hence in a subdirectory). Without this patch running python.exe from the build tree will fail when these tree conditions are true: 1) the CWD is not the root of build tree 2) python.exe is found through $PATH 3) the framework is not yet installed ........ r83099 | richard.jones | 2010-07-23 17:20:40 +0100 (Fri, 23 Jul 2010) | 2 lines New tests for smtpd module. Has full coverage for SMTPChannel and SMTPServer. ........ r83100 | brett.cannon | 2010-07-23 17:22:25 +0100 (Fri, 23 Jul 2010) | 4 lines Make fnmatch be more PEP 8 compliant. Partially closes issue 9356. Thanks to Brian Brazil for the patch. ........ r83101 | brett.cannon | 2010-07-23 17:23:13 +0100 (Fri, 23 Jul 2010) | 4 lines Add tests for fnmatch.filter and translate. Partially closes issue 9356. Thanks to Brian Brazil for the patch. ........ r83102 | brian.curtin | 2010-07-23 17:30:10 +0100 (Fri, 23 Jul 2010) | 3 lines Tab test_repr_daemon over so it's included in ThreadTests. Noticed by Amaury. ........ r83103 | ezio.melotti | 2010-07-23 17:48:22 +0100 (Fri, 23 Jul 2010) | 1 line #9359: fix typo. Thanks to Piotr Kasprzyk for the patch. ........ r83106 | georg.brandl | 2010-07-23 17:55:26 +0100 (Fri, 23 Jul 2010) | 1 line Fix some markup glitches. ........ r83107 | georg.brandl | 2010-07-23 17:55:42 +0100 (Fri, 23 Jul 2010) | 1 line Update to 1.0. ........ r83108 | brett.cannon | 2010-07-23 17:56:21 +0100 (Fri, 23 Jul 2010) | 4 lines Add queue tests for empty, full, put_nowait, and get_nowait. Closes issue 9357. Thanks to Brian Brazil for the patch. ........ r83109 | brett.cannon | 2010-07-23 17:58:21 +0100 (Fri, 23 Jul 2010) | 4 lines Mention in the fnmatch docs that meta-characters in translate cannot be quoted. Closes issue 9358. Thanks to Brian Brazil for the patch. ........ r83111 | brett.cannon | 2010-07-23 17:58:57 +0100 (Fri, 23 Jul 2010) | 2 lines Add Brian Brazil. ........ r83112 | alexander.belopolsky | 2010-07-23 20:25:47 +0100 (Fri, 23 Jul 2010) | 2 lines Issue #7989: Added pure python implementation of the datetime module. ........ r83115 | alexander.belopolsky | 2010-07-23 21:03:53 +0100 (Fri, 23 Jul 2010) | 1 line Use _datetime docstring if _datetime is available. ........ r83116 | victor.stinner | 2010-07-24 01:49:20 +0100 (Sat, 24 Jul 2010) | 4 lines Issue #4629: getopt raises an error if an argument ends with = whereas getopt doesn't except a value (eg. --help= is rejected if getopt uses ['help='] long options). ........ r83120 | victor.stinner | 2010-07-24 03:24:55 +0100 (Sat, 24 Jul 2010) | 12 lines #9032: XML-RPC client: Transport.request() retries on EPIPE error The EPIPE error occurs when the server closes the socket and the client sends a "big" XML-RPC request (I don't know exactly the size threshold). request() just have to ignore the error because single_request() closes the socket on error, and so the next call to single_request() will open a new socket. Remove also a comment in the HTTP client because it's now wrong: see r70643 and issue #5542. ........ r83125 | richard.jones | 2010-07-24 10:51:40 +0100 (Sat, 24 Jul 2010) | 8 lines Implementation for issue 4184 Changes the previously private attributes to make them public, increasing the potential for extending the library in user code. Backward-compatible and documented. ........ r83127 | martin.v.loewis | 2010-07-24 11:09:11 +0100 (Sat, 24 Jul 2010) | 2 lines Put listdir default argument into declaration. ........ r83133 | ronald.oussoren | 2010-07-24 15:15:19 +0100 (Sat, 24 Jul 2010) | 5 lines Fix for issue 9367: the test code for os.getgroups assumes that the result of getgroups and the output of the id(1) command return groups in the same order. That assumption is both fragile and false. ........ r83139 | alexander.belopolsky | 2010-07-25 15:58:54 +0100 (Sun, 25 Jul 2010) | 5 lines Issue #9315: Revert r83005 before renaming test_trace to test_sys_settrace and test_profilehooks to test_sys_setprofile in all three branches. ........ r83140 | alexander.belopolsky | 2010-07-25 16:02:55 +0100 (Sun, 25 Jul 2010) | 5 lines Issue #9315: Renamed test_trace to test_sys_settrace and test_profilehooks to test_sys_setprofile so that test_trace can be used for testing the trace module and for naming consistency. ........ r83141 | alexander.belopolsky | 2010-07-25 16:05:42 +0100 (Sun, 25 Jul 2010) | 1 line Corrected comments on where settrace and setprofile are tested. ........ r83149 | andrew.kuchling | 2010-07-25 23:57:12 +0100 (Sun, 25 Jul 2010) | 1 line #1495229: update the type names used by the XML DOM mapping ........ r83150 | michael.foord | 2010-07-26 00:09:25 +0100 (Mon, 26 Jul 2010) | 1 line Issue #4686 - add .args to exceptions in the configparsermodule ........ r83151 | andrew.kuchling | 2010-07-26 00:23:30 +0100 (Mon, 26 Jul 2010) | 1 line #7637: clarify description of encoding parameter ........ r83152 | andrew.kuchling | 2010-07-26 00:38:47 +0100 (Mon, 26 Jul 2010) | 1 line #777884: make .normalize() do nothing for childless nodes, instead of raising an exception ........ r83154 | brian.curtin | 2010-07-26 01:27:10 +0100 (Mon, 26 Jul 2010) | 5 lines Fix #7113. Patch by ??ukasz Langa. Changes include using a list of lines instead of patching together using string interpolation, and a multi-line value test cases. ........ r83156 | brian.curtin | 2010-07-26 03:36:32 +0100 (Mon, 26 Jul 2010) | 2 lines Add note about #7113 and add ??ukasz Langa to ACKS ........ r83157 | alexander.belopolsky | 2010-07-26 03:36:41 +0100 (Mon, 26 Jul 2010) | 1 line Make python version of fromtimestamp behave more like C. ........ r83160 | georg.brandl | 2010-07-26 09:51:42 +0100 (Mon, 26 Jul 2010) | 1 line #9381: fix markup. ........ r83161 | georg.brandl | 2010-07-26 10:33:12 +0100 (Mon, 26 Jul 2010) | 1 line Add Brian Quinlan. ........ r83162 | andrew.kuchling | 2010-07-26 13:54:02 +0100 (Mon, 26 Jul 2010) | 1 line #7637: fix a grammar error; simplify a sentence ........ r83163 | andrew.kuchling | 2010-07-26 14:08:58 +0100 (Mon, 26 Jul 2010) | 1 line Reword paragraph ........ r83166 | georg.brandl | 2010-07-26 16:11:49 +0100 (Mon, 26 Jul 2010) | 1 line Fix grammar. ........ r83168 | georg.brandl | 2010-07-26 18:00:20 +0100 (Mon, 26 Jul 2010) | 1 line Fix indentation in example. ........ r83169 | georg.brandl | 2010-07-26 18:09:32 +0100 (Mon, 26 Jul 2010) | 1 line Add Reid. ........ r83170 | alexander.belopolsky | 2010-07-26 19:27:49 +0100 (Mon, 26 Jul 2010) | 1 line Added versionadded entry for the annotate argument. ........ r83171 | georg.brandl | 2010-07-26 22:12:13 +0100 (Mon, 26 Jul 2010) | 1 line Clarify. ........ r83173 | gregory.p.smith | 2010-07-27 06:31:29 +0100 (Tue, 27 Jul 2010) | 7 lines The default size of the re module's compiled regular expression cache has been increased from 100 to 500 and the cache replacement policy has changed from simply clearing the entire cache on overflow to randomly forgetting 20% of the existing cached compiled regular expressions. This is a performance win for applications that use a lot of regular expressions and limits the impact of the performance hit anytime the cache is exceeded. ........ r83177 | alexander.belopolsky | 2010-07-27 15:16:32 +0100 (Tue, 27 Jul 2010) | 1 line Issue #9384: python -m tkinter will now display a simple demo applet. ........ r83181 | georg.brandl | 2010-07-27 19:19:21 +0100 (Tue, 27 Jul 2010) | 1 line Update Sphinx to 1.0.1. ........ r83182 | florent.xicluna | 2010-07-27 22:20:15 +0100 (Tue, 27 Jul 2010) | 3 lines Issue #4770: Restrict binascii module to accept only bytes (as specified). And fix the email package to encode to ASCII instead of ``raw-unicode-escape`` before ASCII-to-binary decoding. ........ r83183 | ezio.melotti | 2010-07-27 23:03:33 +0100 (Tue, 27 Jul 2010) | 1 line Use proper skips and assert* methods in test_asyncore. ........ r83184 | antoine.pitrou | 2010-07-27 23:08:27 +0100 (Tue, 27 Jul 2010) | 3 lines Issue #9294: remove dead code in Objects/object.c. Patch by Grant Limberg. ........ r83186 | ezio.melotti | 2010-07-27 23:24:13 +0100 (Tue, 27 Jul 2010) | 1 line With skipUnless there is no need to add test classes conditionally. ........ r83188 | alexander.belopolsky | 2010-07-28 00:02:38 +0100 (Wed, 28 Jul 2010) | 3 lines Issue #9378: python -m pickle will now load and display the first object in the pickle file. ........ r83191 | victor.stinner | 2010-07-28 00:36:41 +0100 (Wed, 28 Jul 2010) | 2 lines Fix ctypes tests to avoid implicit bytes-unicode conversion ........ r83195 | victor.stinner | 2010-07-28 01:15:03 +0100 (Wed, 28 Jul 2010) | 2 lines Issue #8966: ctypes: Remove implicit bytes-unicode conversion ........ r83196 | ezio.melotti | 2010-07-28 01:23:21 +0100 (Wed, 28 Jul 2010) | 1 line Fix failure introduced in r83182. ........ r83197 | victor.stinner | 2010-07-28 01:40:58 +0100 (Wed, 28 Jul 2010) | 2 lines Issue #8991: convertbuffer() rejects discontigious buffers ........ r83201 | georg.brandl | 2010-07-28 09:19:35 +0100 (Wed, 28 Jul 2010) | 1 line #9354: Provide getsockopt() in asyncore file_wrapper(). Patch by Lukas Langa. ........ r83202 | georg.brandl | 2010-07-28 14:13:46 +0100 (Wed, 28 Jul 2010) | 1 line #1682942: add some ConfigParser features: alternate delimiters, alternate comments, empty lines in values. Also enhance the docs with more examples and mention SafeConfigParser before ConfigParser. Patch by Lukas Langa, review by myself, Eric and Ezio. ........ r83209 | senthil.kumaran | 2010-07-28 17:27:56 +0100 (Wed, 28 Jul 2010) | 3 lines Fix Issue6325 - robotparse to honor urls with query strings. ........ r83212 | florent.xicluna | 2010-07-28 17:39:41 +0100 (Wed, 28 Jul 2010) | 2 lines Syntax cleanup. ........ r83213 | georg.brandl | 2010-07-28 18:37:27 +0100 (Wed, 28 Jul 2010) | 1 line Add missing file. ........ r83214 | victor.stinner | 2010-07-28 22:23:23 +0100 (Wed, 28 Jul 2010) | 2 lines #9283: Fix repr(os.environ), display unicode keys and values on POSIX systems ........ r83215 | victor.stinner | 2010-07-28 22:25:42 +0100 (Wed, 28 Jul 2010) | 2 lines Issue #9283: Oops, add missing { and } to repr(os.environ) ........ r83216 | victor.stinner | 2010-07-29 01:29:00 +0100 (Thu, 29 Jul 2010) | 2 lines Update test_os.py according to my last changes on _Environ.__repr__() ........ r83217 | georg.brandl | 2010-07-29 12:15:36 +0100 (Thu, 29 Jul 2010) | 1 line Remove Python 1.5 compatibility note. ........ r83218 | georg.brandl | 2010-07-29 12:49:05 +0100 (Thu, 29 Jul 2010) | 1 line #6538: fix regex documentation again -- use fictional class names "regex" and "match" but do not document them as classes, remove 1.5 compat info and use new default argument syntax where possible. ........ r83219 | georg.brandl | 2010-07-29 12:56:20 +0100 (Thu, 29 Jul 2010) | 1 line Fix for r83202: improve the handling of empty lines. ........ r83220 | georg.brandl | 2010-07-29 13:17:40 +0100 (Thu, 29 Jul 2010) | 1 line #9411: allow selecting an encoding for configparser files. Also adds a new test config file to test special cases. ........ r83222 | georg.brandl | 2010-07-29 14:19:42 +0100 (Thu, 29 Jul 2010) | 1 line Fix #9412: make list of messages an instance attribute instead of class attribute. ........ r83223 | georg.brandl | 2010-07-29 14:38:37 +0100 (Thu, 29 Jul 2010) | 1 line #3874: document HTMLParser.unknown_decl(). ........ r83224 | mark.dickinson | 2010-07-29 14:56:56 +0100 (Thu, 29 Jul 2010) | 1 line Fix typo. ........ r83226 | georg.brandl | 2010-07-29 15:17:12 +0100 (Thu, 29 Jul 2010) | 1 line #1090076: explain the behavior of *vars* in get() better. ........ r83227 | georg.brandl | 2010-07-29 15:23:06 +0100 (Thu, 29 Jul 2010) | 1 line Use Py_CLEAR(). ........ r83229 | georg.brandl | 2010-07-29 15:32:22 +0100 (Thu, 29 Jul 2010) | 1 line #9407: document configparser.Error. ........ r83230 | georg.brandl | 2010-07-29 15:36:11 +0100 (Thu, 29 Jul 2010) | 1 line Use correct directive and name. ........ r83231 | georg.brandl | 2010-07-29 15:46:07 +0100 (Thu, 29 Jul 2010) | 1 line #9397: remove mention of dbm.bsd which does not exist anymore. ........ r83232 | georg.brandl | 2010-07-29 15:49:08 +0100 (Thu, 29 Jul 2010) | 1 line #9388: remove ERA_YEAR which is never defined in the source code. ........ r83234 | georg.brandl | 2010-07-29 17:01:11 +0100 (Thu, 29 Jul 2010) | 1 line #6522: add a "decorator" directive to explicitly document decorators, and use it in a few places. ........ r83235 | victor.stinner | 2010-07-29 17:26:56 +0100 (Thu, 29 Jul 2010) | 2 lines #9397: remove mention of dbm.bsd which does not exist anymore. ........ r83236 | georg.brandl | 2010-07-29 18:16:10 +0100 (Thu, 29 Jul 2010) | 1 line #6630: allow customizing flags for compiling string.Template.idpattern. ........ r83237 | victor.stinner | 2010-07-29 18:19:38 +0100 (Thu, 29 Jul 2010) | 2 lines #8603: Add environb to os.__all__ ........ r83238 | georg.brandl | 2010-07-29 18:55:01 +0100 (Thu, 29 Jul 2010) | 1 line #4108: the first default entry (User-agent: *) wins. ........ r83239 | mark.dickinson | 2010-07-29 22:41:59 +0100 (Thu, 29 Jul 2010) | 2 lines Issue #9422: Fix memory leak when re-initializing a struct.Struct object. ........ r83259 | georg.brandl | 2010-07-30 08:03:39 +0100 (Fri, 30 Jul 2010) | 1 line Clarification. ........ r83260 | georg.brandl | 2010-07-30 08:14:01 +0100 (Fri, 30 Jul 2010) | 1 line #4179: In pdb, allow "list ." as a command to return to the currently debugged line. ........ r83261 | georg.brandl | 2010-07-30 08:21:26 +0100 (Fri, 30 Jul 2010) | 1 line #9230: allow Pdb.checkline() to be called without a current frame, for setting breakpoints before starting debugging. ........ r83262 | georg.brandl | 2010-07-30 09:29:39 +0100 (Fri, 30 Jul 2010) | 1 line #1437051: allow "continue"/"next"/etc. in .pdbrc, also add pdb -c option to give these commands. This allows to run a script until an exception occurs. ........ r83263 | georg.brandl | 2010-07-30 09:43:32 +0100 (Fri, 30 Jul 2010) | 1 line Allow giving an explicit line number to "until". ........ r83264 | georg.brandl | 2010-07-30 09:45:26 +0100 (Fri, 30 Jul 2010) | 1 line Document the "jump" command in pdb.__doc__, and add a version tag for "until X". ........ r83265 | georg.brandl | 2010-07-30 09:54:49 +0100 (Fri, 30 Jul 2010) | 1 line #8015: fix crash when entering an empty line for breakpoint commands. Also restore environment properly when an exception occurs during the definition of commands. ........ r83266 | georg.brandl | 2010-07-30 10:14:20 +0100 (Fri, 30 Jul 2010) | 1 line #1472251: remove addition of "\n" to code given to pdb.run[eval](), the bug in exec() that made this necessary has been fixed. Also document that you can give code objects to run() and runeval(), and add some tests to test_pdb. ........ r83268 | georg.brandl | 2010-07-30 10:23:23 +0100 (Fri, 30 Jul 2010) | 2 lines Issue #8048: Prevent doctests from failing when sys.displayhook has been reassigned. ........ r83269 | georg.brandl | 2010-07-30 10:43:00 +0100 (Fri, 30 Jul 2010) | 1 line #6719: In pdb, do not stop somewhere in the encodings machinery if the source file to be debugged is in a non-builtin encoding. ........ r83270 | georg.brandl | 2010-07-30 10:54:44 +0100 (Fri, 30 Jul 2010) | 1 line Remove redundant import. ........ r83271 | georg.brandl | 2010-07-30 10:59:28 +0100 (Fri, 30 Jul 2010) | 1 line #5727: Restore the ability to use readline when calling into pdb in doctests. ........ r83272 | georg.brandl | 2010-07-30 11:29:19 +0100 (Fri, 30 Jul 2010) | 1 line #5294: Fix the behavior of pdb "continue" command when called in the top-level debugged frame. ........ r83274 | georg.brandl | 2010-07-30 12:31:03 +0100 (Fri, 30 Jul 2010) | 1 line #3143: enable "collapsible sidebar" feature of new Sphinx version. ........ r83275 | georg.brandl | 2010-07-30 13:01:20 +0100 (Fri, 30 Jul 2010) | 1 line #809887: improve pdb feedback for breakpoint-related actions. Also add a functional test for these commands. ........ r83277 | mark.dickinson | 2010-07-30 14:13:02 +0100 (Fri, 30 Jul 2010) | 1 line Add note about surprising behaviour from round function. ........ r83281 | georg.brandl | 2010-07-30 14:36:43 +0100 (Fri, 30 Jul 2010) | 1 line Add myself for pdb. ........ r83283 | georg.brandl | 2010-07-30 15:16:43 +0100 (Fri, 30 Jul 2010) | 1 line #7964 followup: add test case to ensure issue remains fixed. ........ r83284 | georg.brandl | 2010-07-30 16:01:23 +0100 (Fri, 30 Jul 2010) | 1 line Add Breakpoint.bpformat(), which returns the info usually printed by bpprint(). Necessary for major refactoring of pdb output handling. ........ r83285 | georg.brandl | 2010-07-30 16:33:52 +0100 (Fri, 30 Jul 2010) | 1 line pdb now has its own tests. ........ r83286 | georg.brandl | 2010-07-30 17:00:46 +0100 (Fri, 30 Jul 2010) | 7 lines Several enhancements to pdb and its test suite. * added basic test for basic commands * removed duplication of command docs, and moved them to their implementation * unified and useful display of exceptions * output messages and errors using overridable methods (also fixes #1503502) ........ r83287 | georg.brandl | 2010-07-30 18:04:28 +0100 (Fri, 30 Jul 2010) | 1 line Add "longlist" and "source" commands, ideas borrowed from pdb++ by Antonio Cuni. ........ r83288 | martin.v.loewis | 2010-07-30 18:29:39 +0100 (Fri, 30 Jul 2010) | 1 line Drop 2.4 support. Add disabled algorithms to Configure options. ........ r83289 | martin.v.loewis | 2010-07-30 18:30:51 +0100 (Fri, 30 Jul 2010) | 2 lines Use OpenSSL 1.0.0a on Windows. ........ r83291 | georg.brandl | 2010-07-30 19:08:12 +0100 (Fri, 30 Jul 2010) | 1 line Fix source finding if the given frame is a module-level frame. ........ r83292 | georg.brandl | 2010-07-30 19:15:16 +0100 (Fri, 30 Jul 2010) | 1 line Test that "source" with nonexisting things works as expected. ........ r83293 | georg.brandl | 2010-07-30 19:46:38 +0100 (Fri, 30 Jul 2010) | 1 line Show the traceback line numbers as well as the current line numbers if an exception is being debugged. Courtesy of pdb++ by Antonio Cuni. Also document -> and >> markers for "list". ........ r83294 | senthil.kumaran | 2010-07-30 20:34:36 +0100 (Fri, 30 Jul 2010) | 2 lines Fix issue9301 - handle unquote({}) kind of case. ........ r83296 | martin.v.loewis | 2010-07-30 21:03:17 +0100 (Fri, 30 Jul 2010) | 2 lines Import files from zlib 1.2.5. ........ r83307 | matthias.klose | 2010-07-30 22:40:57 +0100 (Fri, 30 Jul 2010) | 2 lines - Issue #7567: PyCurses_setupterm: Don't call `setupterm' twice. ........ r83308 | georg.brandl | 2010-07-30 23:20:16 +0100 (Fri, 30 Jul 2010) | 1 line Part of #7245: when KeyboardInterrupt is raised while defining commands, restore the old commands instead of producing a traceback. ........ r83313 | raymond.hettinger | 2010-07-31 08:12:50 +0100 (Sat, 31 Jul 2010) | 6 lines Only expose the abstract base classes. The concrete types are for internal use (registration). We are not trying to resurrect the types module in collections. ........ r83315 | georg.brandl | 2010-07-31 09:14:16 +0100 (Sat, 31 Jul 2010) | 1 line Fix pdb test failures on the buildbots. ........ r83316 | georg.brandl | 2010-07-31 09:20:02 +0100 (Sat, 31 Jul 2010) | 1 line Make urllib tests pass for now. Will figure out what the correct semantics should be after release. ........ r83317 | georg.brandl | 2010-07-31 09:27:46 +0100 (Sat, 31 Jul 2010) | 1 line Update pydoc topics and adapt Topics builder to Sphinx 1.0. ........ r83318 | georg.brandl | 2010-07-31 09:56:11 +0100 (Sat, 31 Jul 2010) | 1 line Bump versions and review NEWS file. ........ r83319 | florent.xicluna | 2010-07-31 09:56:55 +0100 (Sat, 31 Jul 2010) | 2 lines Fix an oversight in r83294. unquote() should reject bytes. Issue #9301. ........ r83320 | georg.brandl | 2010-07-31 10:01:16 +0100 (Sat, 31 Jul 2010) | 1 line Update copyright years and add releases to release list. Also update Sphinx version number. ........ r83321 | georg.brandl | 2010-07-31 10:03:30 +0100 (Sat, 31 Jul 2010) | 1 line At least give IDLE 3.1 a release date. No further entries there for 3.2 though. ........ r83322 | tarek.ziade | 2010-07-31 10:10:51 +0100 (Sat, 31 Jul 2010) | 1 line reverted distutils doc to its 3.1 state ........ r83323 | georg.brandl | 2010-07-31 10:15:10 +0100 (Sat, 31 Jul 2010) | 1 line After distutils doc reversal, change back **bold todo** items to XXX comments. ........ r83324 | martin.v.loewis | 2010-07-31 10:21:51 +0100 (Sat, 31 Jul 2010) | 2 lines Update OpenSSL version. ........ r83325 | georg.brandl | 2010-07-31 10:37:03 +0100 (Sat, 31 Jul 2010) | 1 line Copy Sun-specific inclusion of from 2.7 maint to trunk; it seems to not have been merged to py3k. ........ r83326 | georg.brandl | 2010-07-31 11:08:09 +0100 (Sat, 31 Jul 2010) | 1 line Avoid triggering DeprecationWarnings in test_smtpd and smtpd. ........ r83327 | raymond.hettinger | 2010-07-31 11:11:39 +0100 (Sat, 31 Jul 2010) | 1 line Add functools.lfu_cache() and functools.lru_cache(). ........ r83328 | raymond.hettinger | 2010-07-31 11:14:41 +0100 (Sat, 31 Jul 2010) | 1 line Document how to change OrderedDict update order from first to last. ........ r83329 | georg.brandl | 2010-07-31 11:16:21 +0100 (Sat, 31 Jul 2010) | 1 line Revert r83327. This will have to wait until after the alpha1 release. ........ r83335 | martin.v.loewis | 2010-07-31 11:49:53 +0100 (Sat, 31 Jul 2010) | 1 line Copy asm files into place. ........ r83336 | martin.v.loewis | 2010-07-31 11:50:16 +0100 (Sat, 31 Jul 2010) | 2 lines Delete openssl checkouts. ........ r83337 | victor.stinner | 2010-07-31 11:52:56 +0100 (Sat, 31 Jul 2010) | 4 lines Issue #8966: Fix ctypes tests for Windows I removed the implicit conversion from str to bytes. ........ r83338 | martin.v.loewis | 2010-07-31 11:56:53 +0100 (Sat, 31 Jul 2010) | 2 lines Truly uncomment rm lines. ........ r83339 | georg.brandl | 2010-07-31 12:00:47 +0100 (Sat, 31 Jul 2010) | 1 line Rewrap. ........ r83341 | georg.brandl | 2010-07-31 12:40:07 +0100 (Sat, 31 Jul 2010) | 1 line #9430: document timedelta str() and repr(). ........ r83342 | georg.brandl | 2010-07-31 12:52:46 +0100 (Sat, 31 Jul 2010) | 1 line Import test_pdb with its full name, so that running python -m test.test_pdb succeeds. ........ r83343 | georg.brandl | 2010-07-31 13:06:51 +0100 (Sat, 31 Jul 2010) | 1 line From Martin: New UUIDs for the 3.2 release series. ........ r83348 | georg.brandl | 2010-07-31 19:05:35 +0100 (Sat, 31 Jul 2010) | 1 line Post-release updates. ........ r83349 | antoine.pitrou | 2010-07-31 19:08:33 +0100 (Sat, 31 Jul 2010) | 3 lines Add ssl changes to the 3.2 "what's new". ........ r83350 | georg.brandl | 2010-07-31 19:09:23 +0100 (Sat, 31 Jul 2010) | 1 line Re-commit r83327 now that the release is done. ........ r83351 | georg.brandl | 2010-07-31 19:09:46 +0100 (Sat, 31 Jul 2010) | 1 line Move news item to the correct position. ........ r83352 | georg.brandl | 2010-07-31 19:11:07 +0100 (Sat, 31 Jul 2010) | 1 line #9440: Remove borderline test case that fails based on unpredictable conditions such as compiler flags. ........ r83353 | martin.v.loewis | 2010-07-31 19:59:20 +0100 (Sat, 31 Jul 2010) | 1 line Drop webchecker and BerkeleyDB license. ........ r83355 | georg.brandl | 2010-07-31 20:17:11 +0100 (Sat, 31 Jul 2010) | 1 line Fix bad merge: test_support -> support. ........ r83356 | georg.brandl | 2010-07-31 20:29:15 +0100 (Sat, 31 Jul 2010) | 1 line Remove trailing whitespace. ........ r83357 | georg.brandl | 2010-07-31 20:59:55 +0100 (Sat, 31 Jul 2010) | 1 line #5778: document that sys.version can contain a newline. ........ r83358 | georg.brandl | 2010-07-31 21:05:31 +0100 (Sat, 31 Jul 2010) | 1 line #9442: do not document a specific format for sys.version; rather refer to version_info and the platform module. ........ r83359 | georg.brandl | 2010-07-31 21:08:15 +0100 (Sat, 31 Jul 2010) | 1 line #1286: allow using fileinput.FileInput as context manager. ........ r83360 | georg.brandl | 2010-07-31 21:13:44 +0100 (Sat, 31 Jul 2010) | 1 line Clarify comment in comments test case explaining comment semantics. ........ r83361 | georg.brandl | 2010-07-31 22:04:00 +0100 (Sat, 31 Jul 2010) | 1 line #3788: more tests for http.cookies, now at 95% coverage. Also bring coding style in the module up to PEP 8, where it does not break backwards compatibility. ........ r83362 | georg.brandl | 2010-07-31 22:12:15 +0100 (Sat, 31 Jul 2010) | 1 line #8910: add a file explaining why Lib/test/data is there. ........ r83366 | georg.brandl | 2010-07-31 22:26:40 +0100 (Sat, 31 Jul 2010) | 1 line There always is a False and True now. ........ r83368 | georg.brandl | 2010-07-31 22:40:15 +0100 (Sat, 31 Jul 2010) | 1 line #7909: the prefixes \\.\ and \\?\ indicate special Windows paths, do not try to manipulate them. See http://msdn.microsoft.com/en-us/library/aa365247%28VS.85%29.aspx for details. ........ r83369 | georg.brandl | 2010-07-31 22:41:42 +0100 (Sat, 31 Jul 2010) | 1 line Fix "Berkeley" name. ........ r83370 | georg.brandl | 2010-07-31 22:51:48 +0100 (Sat, 31 Jul 2010) | 5 lines #8198: the Helper class should not save the stdin and stdout objects at import time, rather by default use the current streams like the other APIs that output help. ........ r83371 | georg.brandl | 2010-07-31 22:54:24 +0100 (Sat, 31 Jul 2010) | 1 line #8292: Fix three instances of truth tests on return values of filter() (which is always true in Python 3). ........ r83372 | georg.brandl | 2010-07-31 23:05:54 +0100 (Sat, 31 Jul 2010) | 1 line #4007: remove *.a and *.so.X.Y files in "make clean". ........ r83373 | georg.brandl | 2010-07-31 23:11:11 +0100 (Sat, 31 Jul 2010) | 1 line #5147: revert accidental indentation of header constant for MozillaCookieJar. ........ r83374 | georg.brandl | 2010-07-31 23:32:52 +0100 (Sat, 31 Jul 2010) | 1 line #5146: handle UID THREAD command correctly. ........ r83375 | antoine.pitrou | 2010-07-31 23:48:02 +0100 (Sat, 31 Jul 2010) | 3 lines Reorder entries by module lexicographic order ........ r83376 | raymond.hettinger | 2010-08-01 00:33:22 +0100 (Sun, 01 Aug 2010) | 1 line Fix build on VS8. ........ r83380 | r.david.murray | 2010-08-01 04:31:09 +0100 (Sun, 01 Aug 2010) | 17 lines #8620: Cmd no longer truncates last character if stdin ends without newline Cmd used to blindly chop off the last character of every input line. If the input reached EOF and there was no final new line, it would truncate the last character of the last command. This fix instead strips trailing \r\n from the input lines. While this is a small behavior change, it should not break any working code, since feeding a '\r\n' terminated file to Cmd would previously leave the \r's on the lines, resulting in failed command execution. I wrote the unit test in preparation for a PyOhio TeachMe session run by Catherine Devlin, and we can thank Catherine and the PyOhio session attendees for the fix. I've added Catherine to the Acks file for organizing and leading the TeachMe session, out of which we will hopefully get some new contributors. ........ r83384 | georg.brandl | 2010-08-01 07:32:55 +0100 (Sun, 01 Aug 2010) | 1 line Build properties using lambdas. This makes test_pyclbr pass again, because it does not think that input and output are methods anymore. ........ r83385 | georg.brandl | 2010-08-01 07:42:45 +0100 (Sun, 01 Aug 2010) | 1 line #8773: mailbox.py does not need to be executable. ........ r83386 | georg.brandl | 2010-08-01 07:44:46 +0100 (Sun, 01 Aug 2010) | 1 line #8768: name test method properly so that it gets executed. ........ r83387 | georg.brandl | 2010-08-01 07:53:28 +0100 (Sun, 01 Aug 2010) | 1 line #8735: better explain semantics of *values* argument for parse(). ........ r83388 | georg.brandl | 2010-08-01 08:48:43 +0100 (Sun, 01 Aug 2010) | 1 line #7395: fix traceback in do_add() when no stats are loaded. Apply same fix for do_sort() and do_reverse(). ........ r83389 | georg.brandl | 2010-08-01 08:57:47 +0100 (Sun, 01 Aug 2010) | 1 line Small improvements to pstats browser: do not crash on reading invalid file, and actually do a reload when executing "read" as intended. ........ r83390 | georg.brandl | 2010-08-01 09:07:49 +0100 (Sun, 01 Aug 2010) | 1 line #8230: make Lib/test/sortperf.py run on Python 3. ........ r83391 | georg.brandl | 2010-08-01 09:10:08 +0100 (Sun, 01 Aug 2010) | 1 line Add another news entry. ........ r83393 | georg.brandl | 2010-08-01 09:35:29 +0100 (Sun, 01 Aug 2010) | 1 line #1690103: fix initial namespace for code run with trace.main(). ........ r83394 | georg.brandl | 2010-08-01 09:46:24 +0100 (Sun, 01 Aug 2010) | 1 line No need to split this, there are enough long lines. ........ r83395 | georg.brandl | 2010-08-01 09:49:18 +0100 (Sun, 01 Aug 2010) | 1 line #8821: do not rely on Unicode strings being terminated with a \u0000, rather explicitly check range before looking for a second surrogate character. ........ r83396 | georg.brandl | 2010-08-01 09:52:32 +0100 (Sun, 01 Aug 2010) | 1 line #4810: document "--" option separator in timeit help. ........ r83397 | ronald.oussoren | 2010-08-01 10:02:50 +0100 (Sun, 01 Aug 2010) | 1 line Ensure that test_site actually passes with a framework build ........ r83398 | georg.brandl | 2010-08-01 10:06:34 +0100 (Sun, 01 Aug 2010) | 1 line #8826: the "expires" attribute value is a date string with spaces, but apparently not all user-agents put it in quotes. Handle that as a special case. ........ r83399 | georg.brandl | 2010-08-01 10:17:53 +0100 (Sun, 01 Aug 2010) | 1 line Package some new files that are needed for running the test suite from the MSI package. ........ r83400 | mark.dickinson | 2010-08-01 11:41:49 +0100 (Sun, 01 Aug 2010) | 7 lines Issue #9416: Fix some issues with complex formatting where the output with no type specifier failed to match the str output: - format(complex(-0.0, 2.0), '-') omitted the real part from the output, - format(complex(0.0, 2.0), '-') included a sign and parentheses. ........ r83403 | mark.dickinson | 2010-08-01 12:10:28 +0100 (Sun, 01 Aug 2010) | 1 line Add test for memory leak reported in issue 9422. ........ r83404 | georg.brandl | 2010-08-01 15:25:22 +0100 (Sun, 01 Aug 2010) | 1 line #6439: fix argument type for PySys_SetArgvEx() and Py_SetProgramName() in Demo/embed code. ........ r83405 | georg.brandl | 2010-08-01 15:38:17 +0100 (Sun, 01 Aug 2010) | 1 line #4943: do not try to include drive letters (and colons) when looking for a probably module name. ........ r83406 | georg.brandl | 2010-08-01 15:50:00 +0100 (Sun, 01 Aug 2010) | 1 line #8046: add context manager protocol support to mmap objects. Also add closed property. ........ r83407 | brian.curtin | 2010-08-01 16:26:26 +0100 (Sun, 01 Aug 2010) | 3 lines Fix #8105. Add validation to mmap.mmap so invalid file descriptors don't cause a crash on Windows. ........ r83408 | georg.brandl | 2010-08-01 16:30:56 +0100 (Sun, 01 Aug 2010) | 1 line #5551: symbolic links never can be mount points. Fixes the fix for #1713. ........ r83411 | antoine.pitrou | 2010-08-01 17:53:42 +0100 (Sun, 01 Aug 2010) | 4 lines Issue #9448: Fix a leak of OS resources (mutexes or semaphores) when re-initializing a buffered IO object by calling its `__init__` method. ........ r83415 | senthil.kumaran | 2010-08-01 18:53:37 +0100 (Sun, 01 Aug 2010) | 3 lines Fix Issue8123 - TypeError in urllib when trying to use HTTP authentication ........ r83417 | georg.brandl | 2010-08-01 19:38:26 +0100 (Sun, 01 Aug 2010) | 1 line #5776: fix mistakes in python specfile. (Nobody probably uses it anyway.) ........ r83431 | ronald.oussoren | 2010-08-01 20:18:13 +0100 (Sun, 01 Aug 2010) | 6 lines test_getgroups as introduced with issue7900 failed on systems where 'id -G' and posix.getgroups() returned the same information, but one of the sources contains duplicate information. Rewrite the check using sets instead of lists. ........ r83440 | antoine.pitrou | 2010-08-01 21:08:46 +0100 (Sun, 01 Aug 2010) | 4 lines Issue #8397: Raise an error when attempting to mix iteration and regular reads on a BZ2File object, rather than returning incorrect results. ........ r83444 | georg.brandl | 2010-08-01 21:51:02 +0100 (Sun, 01 Aug 2010) | 1 line Revert r83395, it introduces test failures and is not necessary anyway since we now have to nul-terminate the string anyway. ........ r83456 | raymond.hettinger | 2010-08-01 22:10:35 +0100 (Sun, 01 Aug 2010) | 1 line Issue 9445: Fix undefined symbols on VS8.0 build. ........ r83479 | mark.dickinson | 2010-08-01 22:33:01 +0100 (Sun, 01 Aug 2010) | 1 line Don't delete Lib/test/data/README when doing 'make distclean' ........ r83488 | raymond.hettinger | 2010-08-01 22:50:38 +0100 (Sun, 01 Aug 2010) | 1 line Fix VS8.0 build by adding _time.h and _time.c to the project file. ........ r83494 | raymond.hettinger | 2010-08-01 23:10:57 +0100 (Sun, 01 Aug 2010) | 1 line Update OrderedDict implementation to match that in Py2.7. ........ r83501 | georg.brandl | 2010-08-01 23:31:05 +0100 (Sun, 01 Aug 2010) | 1 line Fix style of referring to issues. ........ r83506 | r.david.murray | 2010-08-02 00:43:28 +0100 (Mon, 02 Aug 2010) | 2 lines Fix ACKS alphabetization. ........ r83513 | raymond.hettinger | 2010-08-02 01:59:14 +0100 (Mon, 02 Aug 2010) | 1 line Add example applications for the LRU and LFU cache docs. ........ r83516 | raymond.hettinger | 2010-08-02 02:43:41 +0100 (Mon, 02 Aug 2010) | 1 line Demonstrate the caching decorators in whatsnew. ........ r83521 | senthil.kumaran | 2010-08-02 12:04:58 +0100 (Mon, 02 Aug 2010) | 3 lines Fix Issue8572 - httplib getheader() throws error instead of default ........ r83523 | georg.brandl | 2010-08-02 13:06:18 +0100 (Mon, 02 Aug 2010) | 1 line #9209 and #7781: fix two crashes in pstats interactive browser. ........ r83524 | georg.brandl | 2010-08-02 13:20:23 +0100 (Mon, 02 Aug 2010) | 1 line #9428: fix running scripts from profile/cProfile with their own name and the right namespace. Same fix as for trace.py in #1690103. ........ r83525 | georg.brandl | 2010-08-02 13:36:24 +0100 (Mon, 02 Aug 2010) | 1 line Get rid of spurious "threading" entries in trace output. ........ r83526 | georg.brandl | 2010-08-02 13:40:22 +0100 (Mon, 02 Aug 2010) | 1 line Fix softspace relic. ........ r83527 | georg.brandl | 2010-08-02 13:48:46 +0100 (Mon, 02 Aug 2010) | 1 line #3821: beginnings of a trace.py unittest. ........ r83528 | georg.brandl | 2010-08-02 13:54:24 +0100 (Mon, 02 Aug 2010) | 1 line Document how to refer to decorators and decorator methods. ........ r83529 | senthil.kumaran | 2010-08-02 18:09:02 +0100 (Mon, 02 Aug 2010) | 3 lines Rewording the getheader method of HTTPResponse. ........ r83531 | georg.brandl | 2010-08-02 18:24:49 +0100 (Mon, 02 Aug 2010) | 1 line #7372: fix regression in pstats: a previous fix to handle cProfile data in add_callers broke handling of profile data. ........ r83536 | georg.brandl | 2010-08-02 18:49:25 +0100 (Mon, 02 Aug 2010) | 1 line #8578: mention danger of not incref'ing weak referenced object. ........ r83538 | georg.brandl | 2010-08-02 19:10:13 +0100 (Mon, 02 Aug 2010) | 1 line #6928: fix class docs w.r.t. new metaclasses. ........ r83542 | georg.brandl | 2010-08-02 19:56:54 +0100 (Mon, 02 Aug 2010) | 1 line Move test_SimpleHTTPServer into test_httpservers. ........ r83543 | georg.brandl | 2010-08-02 19:59:52 +0100 (Mon, 02 Aug 2010) | 1 line #8560: add progress indicator to regrtest. ........ r83546 | georg.brandl | 2010-08-02 20:16:34 +0100 (Mon, 02 Aug 2010) | 1 line #7973: Fix distutils options spelling. ........ r83547 | georg.brandl | 2010-08-02 20:19:26 +0100 (Mon, 02 Aug 2010) | 1 line #7386: add example that shows that trailing path separators are stripped. ........ r83548 | georg.brandl | 2010-08-02 20:23:34 +0100 (Mon, 02 Aug 2010) | 1 line #8172: how does one use a property? ........ r83550 | georg.brandl | 2010-08-02 20:32:43 +0100 (Mon, 02 Aug 2010) | 1 line #9451: strengthen warning about __*__ special name usage. ........ r83551 | georg.brandl | 2010-08-02 20:35:06 +0100 (Mon, 02 Aug 2010) | 1 line Remove XXX comment that was displayed. ........ r83552 | georg.brandl | 2010-08-02 20:36:36 +0100 (Mon, 02 Aug 2010) | 1 line #9438: clarify that constant names also cannot be assigned as attributes. ........ r83553 | georg.brandl | 2010-08-02 20:39:17 +0100 (Mon, 02 Aug 2010) | 1 line Remove redundant information. ........ r83554 | georg.brandl | 2010-08-02 20:43:05 +0100 (Mon, 02 Aug 2010) | 1 line #7280: note about nasmw.exe. ........ r83555 | georg.brandl | 2010-08-02 20:44:48 +0100 (Mon, 02 Aug 2010) | 1 line #8861: remove unused variable. ........ r83558 | georg.brandl | 2010-08-02 21:05:19 +0100 (Mon, 02 Aug 2010) | 1 line #8648: document UTF-7 codec functions. ........ r83560 | georg.brandl | 2010-08-02 21:16:18 +0100 (Mon, 02 Aug 2010) | 1 line #9087: update json docstrings -- unicode and long do not exist anymore. ........ r83561 | georg.brandl | 2010-08-02 21:17:50 +0100 (Mon, 02 Aug 2010) | 1 line #4280: remove outdated "versionchecker" tool. ........ r83563 | georg.brandl | 2010-08-02 21:21:21 +0100 (Mon, 02 Aug 2010) | 1 line #9037: add example how to raise custom exceptions from C code. ........ r83565 | georg.brandl | 2010-08-02 21:27:20 +0100 (Mon, 02 Aug 2010) | 1 line #9111: document that do_help() looks at docstrings. ........ r83566 | georg.brandl | 2010-08-02 21:30:57 +0100 (Mon, 02 Aug 2010) | 1 line #9019: remove false (in 3k) claim about Headers updates. ........ r83569 | georg.brandl | 2010-08-02 21:39:35 +0100 (Mon, 02 Aug 2010) | 1 line #7797: be explicit about bytes-oriented interface of base64 functions. ........ r83571 | georg.brandl | 2010-08-02 21:44:34 +0100 (Mon, 02 Aug 2010) | 1 line Clarify that abs() is not a namespace. ........ r83574 | georg.brandl | 2010-08-02 21:47:56 +0100 (Mon, 02 Aug 2010) | 1 line #6867: epoll.register() returns None. ........ r83575 | georg.brandl | 2010-08-02 21:52:10 +0100 (Mon, 02 Aug 2010) | 1 line #9238: zipfile does handle archive comments. ........ r83580 | georg.brandl | 2010-08-02 22:02:36 +0100 (Mon, 02 Aug 2010) | 1 line #8119: fix copy-paste error. ........ r83584 | georg.brandl | 2010-08-02 22:07:14 +0100 (Mon, 02 Aug 2010) | 1 line #9457: fix documentation links for 3.2. ........ r83599 | georg.brandl | 2010-08-02 22:51:18 +0100 (Mon, 02 Aug 2010) | 1 line #9061: warn that single quotes are never escaped. ........ r83605 | georg.brandl | 2010-08-02 23:08:58 +0100 (Mon, 02 Aug 2010) | 1 line Make the Pynche tool work with Python 3. ........ r83606 | georg.brandl | 2010-08-02 23:25:16 +0100 (Mon, 02 Aug 2010) | 1 line Minimum fixes to make freeze.py do something useful. ........ r83607 | georg.brandl | 2010-08-02 23:28:01 +0100 (Mon, 02 Aug 2010) | 1 line Remove faqwiz tool. ........ r83608 | georg.brandl | 2010-08-02 23:31:22 +0100 (Mon, 02 Aug 2010) | 1 line Update README for Tools. ........ r83609 | georg.brandl | 2010-08-02 23:53:22 +0100 (Mon, 02 Aug 2010) | 1 line Update README, remove obsolete scripts. ........ r83610 | georg.brandl | 2010-08-02 23:55:17 +0100 (Mon, 02 Aug 2010) | 1 line Update README, remove obsolete script. ........ r83611 | georg.brandl | 2010-08-02 23:58:25 +0100 (Mon, 02 Aug 2010) | 1 line Make the framer run (still generates wrong code for module creation though.) ........ r83612 | georg.brandl | 2010-08-02 23:59:44 +0100 (Mon, 02 Aug 2010) | 1 line Fix unicode literal. ........ r83613 | georg.brandl | 2010-08-03 00:13:12 +0100 (Tue, 03 Aug 2010) | 1 line Update Demo README. ........ r83614 | georg.brandl | 2010-08-03 00:13:24 +0100 (Tue, 03 Aug 2010) | 1 line Make minigzip work again. ........ r83615 | georg.brandl | 2010-08-03 00:15:58 +0100 (Tue, 03 Aug 2010) | 1 line Another demo that at least runs again. ........ r83616 | georg.brandl | 2010-08-03 00:17:21 +0100 (Tue, 03 Aug 2010) | 1 line Remove obsolete pdist demo. ........ r83617 | georg.brandl | 2010-08-03 00:18:35 +0100 (Tue, 03 Aug 2010) | 1 line Remove reference to removed faqwiz tool. ........ r83618 | georg.brandl | 2010-08-03 00:30:09 +0100 (Tue, 03 Aug 2010) | 1 line Fix-up some tkinter demos. ........ r83636 | richard.jones | 2010-08-03 07:39:33 +0100 (Tue, 03 Aug 2010) | 2 lines improvements to test_smtplib per issue2423 merged the socket mock introduced in test_smtpd ........ r83644 | ronald.oussoren | 2010-08-03 08:42:42 +0100 (Tue, 03 Aug 2010) | 2 lines Fix for issue 9455: platform.mac_ver() broken on OSX/ppc ........ r83649 | georg.brandl | 2010-08-03 08:56:50 +0100 (Tue, 03 Aug 2010) | 1 line Fix regrtest -F. ........ r83659 | georg.brandl | 2010-08-03 13:06:29 +0100 (Tue, 03 Aug 2010) | 1 line Terminology fix: exceptions are raised, except in generator.throw(). ........ r83660 | georg.brandl | 2010-08-03 13:31:59 +0100 (Tue, 03 Aug 2010) | 1 line Get rid of UserWarnings when running Sphinx from tools dir. ........ r83663 | georg.brandl | 2010-08-03 13:36:57 +0100 (Tue, 03 Aug 2010) | 1 line A couple of nits how to ignore errors. ........ r83667 | mark.dickinson | 2010-08-03 17:08:16 +0100 (Tue, 03 Aug 2010) | 2 lines Issue #9450: Fix memory leaks in readline.remove/replace_history_entry. ........ r83670 | mark.dickinson | 2010-08-03 17:49:49 +0100 (Tue, 03 Aug 2010) | 3 lines Issue #8065: Fix another memory leak in readline module, from failure to free the result of a call to history_get_history_state. ........ r83673 | antoine.pitrou | 2010-08-03 18:09:36 +0100 (Tue, 03 Aug 2010) | 4 lines Issue #8867: Fix `Tools/scripts/serve.py` to work with files containing non-ASCII content. ........ r83675 | r.david.murray | 2010-08-03 18:56:09 +0100 (Tue, 03 Aug 2010) | 12 lines #9444: use first of prefix_chars for help opt instead of raising error An argparse option parser created with a prefix_chars that did not include a '-' would happily add -h and --help options, and then throw an error when it tried to format the help because the - was an invalid prefix character. This patch makes it use the first character of prefix_chars as the character for the help options if and only if '-' is not one of the valid prefix_chars. Fix by Theodore Turocy, unit tests by Catherine Devlin. ........ r83677 | mark.dickinson | 2010-08-03 19:31:54 +0100 (Tue, 03 Aug 2010) | 1 line Fix memory leak in ssl module. ........ r83678 | antoine.pitrou | 2010-08-03 19:32:26 +0100 (Tue, 03 Aug 2010) | 4 lines In test_threading_local, test both the default _thread._local implementation and the pure Python implementation in Lib/_threading_local.py ........ r83681 | martin.v.loewis | 2010-08-03 19:35:55 +0100 (Tue, 03 Aug 2010) | 2 lines Add various missing files. Improve detection of unpackaged files. ........ r83683 | mark.dickinson | 2010-08-03 19:44:16 +0100 (Tue, 03 Aug 2010) | 1 line Misc/NEWS entry for r83677. ........ r83690 | r.david.murray | 2010-08-03 23:14:10 +0100 (Tue, 03 Aug 2010) | 10 lines #3196: if needed pad a short base64 encoded word before trying to decode. The RFCs encourage following Postel's law: be liberal in what you accept. So if someone forgot to pad the base64 encoded word payload to an even four bytes, we add the padding before handing it to base64mime.decode. Previously, missing padding resulted in a HeaderParseError. Patch by Jason Williams. ........ r83696 | antoine.pitrou | 2010-08-04 01:18:49 +0100 (Wed, 04 Aug 2010) | 3 lines That test was never run (since thread has been renamed to _thread in 3.x) ........ r83698 | richard.jones | 2010-08-04 02:19:22 +0100 (Wed, 04 Aug 2010) | 1 line note smtpd module changes in NEWS ........ r83699 | richard.jones | 2010-08-04 02:20:14 +0100 (Wed, 04 Aug 2010) | 1 line improve smtpd module test coverage ........ r83701 | senthil.kumaran | 2010-08-04 05:50:44 +0100 (Wed, 04 Aug 2010) | 3 lines Fix Issue754016 - urlparse goes wrong with IP:port without scheme ........ r83705 | giampaolo.rodola | 2010-08-04 10:02:27 +0100 (Wed, 04 Aug 2010) | 1 line fix issue #2944: asyncore doesn't handle connection refused correctly (patch by Alexander Shigin). Merged from 2.7 branch. ........ r83707 | giampaolo.rodola | 2010-08-04 10:28:05 +0100 (Wed, 04 Aug 2010) | 1 line issue #8687: provides a test suite for sched.py module ........ r83708 | giampaolo.rodola | 2010-08-04 11:12:00 +0100 (Wed, 04 Aug 2010) | 1 line fix issue #6822: ftplib's storline method doesn't work with text files ........ r83709 | antoine.pitrou | 2010-08-04 11:26:30 +0100 (Wed, 04 Aug 2010) | 3 lines Something fun to maintain ........ r83712 | giampaolo.rodola | 2010-08-04 11:36:18 +0100 (Wed, 04 Aug 2010) | 1 line as per discussion with antoine revert changes made in 83708 as the user useing ftplib's readline methods is supposed to always use a binary file ........ r83713 | antoine.pitrou | 2010-08-04 12:48:56 +0100 (Wed, 04 Aug 2010) | 3 lines Factor out stripping of interpreter debug output in test.support.strip_python_stderr() ........ r83714 | richard.jones | 2010-08-04 13:27:36 +0100 (Wed, 04 Aug 2010) | 1 line fix test_smtplib/test_smtpd collision through pre-loaded reply data in mock_socket ........ r83715 | antoine.pitrou | 2010-08-04 14:24:41 +0100 (Wed, 04 Aug 2010) | 3 lines I'm interested in threading issues as well ........ r83719 | antoine.pitrou | 2010-08-04 16:43:16 +0100 (Wed, 04 Aug 2010) | 4 lines Issue #9496: Provide a test suite for the rlcompleter module. Patch by Michele Orr??. ........ r83722 | brian.curtin | 2010-08-04 16:47:24 +0100 (Wed, 04 Aug 2010) | 7 lines Fix #9513 to remove relative imports from multiprocessing. The test suite currently skips test_multiprocessing on Windows because it fails on finding _multiprocessing in several win32-specific blocks. Removing the relative import lets this through and allows the test to run (and pass). ........ r83726 | antoine.pitrou | 2010-08-04 17:45:21 +0100 (Wed, 04 Aug 2010) | 3 lines In verbose mode, identify OpenSSL build and platform more precisely ........ r83727 | antoine.pitrou | 2010-08-04 18:14:06 +0100 (Wed, 04 Aug 2010) | 3 lines Try to fix issue #9415: skip some tests on broken Ubuntu OpenSSL ........ r83729 | senthil.kumaran | 2010-08-04 18:46:23 +0100 (Wed, 04 Aug 2010) | 3 lines Sending the auth info as string. Fix BytesWarning: str() on a bytes instance Exception on buildbot. ........ r83731 | antoine.pitrou | 2010-08-04 19:28:02 +0100 (Wed, 04 Aug 2010) | 5 lines Issue #8814: function annotations (the `__annotations__` attribute) are now included in the set of attributes copied by default by functools.wraps and functools.update_wrapper. Patch by Terrence Cole. ........ r83732 | mark.dickinson | 2010-08-04 19:42:43 +0100 (Wed, 04 Aug 2010) | 3 lines Issue #9498: Add reference to sys.float_info from 'numeric types' docs. Thanks Yitz Gale. ........ r83735 | martin.v.loewis | 2010-08-04 20:08:20 +0100 (Wed, 04 Aug 2010) | 2 lines Add Terry Reedy. ........ r83736 | mark.dickinson | 2010-08-04 21:56:28 +0100 (Wed, 04 Aug 2010) | 3 lines Issue #9337: Make float.__str__ identical to float.__repr__. (And similarly for complex numbers.) ........ r83741 | alexandre.vassalotti | 2010-08-05 08:12:18 +0100 (Thu, 05 Aug 2010) | 4 lines Issue 5077: Add documentation for operator fixer. Patch by Meador Inge. ........ r83742 | gerhard.haering | 2010-08-05 15:08:44 +0100 (Thu, 05 Aug 2010) | 6 lines Issue #6683: For SMTP logins we now try all authentication methods advertised by the server. Many servers are buggy and advertise authentication methods they o not support in reality. This change makes smtplib.auth() work more often in the real world, where we face misconfigured servers and servers that advertise methods they don't support due to the madness that is SASL. ........ r83744 | alexander.belopolsky | 2010-08-05 18:34:27 +0100 (Thu, 05 Aug 2010) | 5 lines Issue #9079: Added _PyTime_gettimeofday(_PyTime_timeval *tp) to C API exposed in Python.h. This function is similar to POSIX gettimeofday(struct timeval *tp), but available on platforms without gettimeofday(). ........ r83745 | brian.curtin | 2010-08-05 19:56:00 +0100 (Thu, 05 Aug 2010) | 4 lines Issue #9524: Document that two CTRL* signals are meant for use only with os.kill. ........ r83747 | gerhard.haering | 2010-08-06 07:12:05 +0100 (Fri, 06 Aug 2010) | 2 lines Issue #3854: Documented using the sqlite3 module with multiple threads. ........ r83750 | raymond.hettinger | 2010-08-06 10:22:46 +0100 (Fri, 06 Aug 2010) | 1 line Fix the VS8.0 build ........ r83751 | mark.dickinson | 2010-08-06 10:36:57 +0100 (Fri, 06 Aug 2010) | 1 line Issue #9526: Remove outdated casts to int that were preventing the array module from working correctly with arrays > 2GB. ........ r83752 | mark.dickinson | 2010-08-06 10:38:58 +0100 (Fri, 06 Aug 2010) | 1 line Misc/NEWS entry for r83751. ........ r83755 | raymond.hettinger | 2010-08-06 10:52:17 +0100 (Fri, 06 Aug 2010) | 1 line Issue8757: Implicit set-to-frozenset conversion not thread-safe. ........ r83758 | martin.v.loewis | 2010-08-06 11:43:31 +0100 (Fri, 06 Aug 2010) | 1 line Change pyc removal to support __pycache__. ........ r83759 | tim.golden | 2010-08-06 14:03:56 +0100 (Fri, 06 Aug 2010) | 1 line Issue #3210: Ensure stdio handles are closed if CreateProcess fails ........ r83762 | mark.dickinson | 2010-08-06 19:55:26 +0100 (Fri, 06 Aug 2010) | 1 line In PySlice_IndicesEx, clip the step to [-PY_SSIZE_T_MAX, PY_SSIZE_T_MAX] rather than [PY_SSIZE_T_MIN, PY_SSIZE_T_MAX]. ........ r83763 | brian.curtin | 2010-08-06 20:27:32 +0100 (Fri, 06 Aug 2010) | 3 lines Fix #9324: Add parameter validation to signal.signal on Windows in order to prevent crashes. ........ r83767 | r.david.murray | 2010-08-06 22:18:49 +0100 (Fri, 06 Aug 2010) | 4 lines Add xml subpackages, since they are likely to have different maintainers. And if not they can be merged back together when that becomes apparent. ........ r83768 | mark.dickinson | 2010-08-06 22:33:18 +0100 (Fri, 06 Aug 2010) | 3 lines Issue #9530: Fix a couple of places where undefined behaviour can occur, as a result of signed integer overflow. ........ r83770 | raymond.hettinger | 2010-08-07 00:23:49 +0100 (Sat, 07 Aug 2010) | 1 line Improve the whatsnew article on the lru/lfu cache decorators. ........ r83771 | brian.curtin | 2010-08-07 04:47:21 +0100 (Sat, 07 Aug 2010) | 3 lines Fix an assertRaises situation and typo. Also pass all tests to run_unittest rather than do it by platform -- the proper skips are in place already. ........ r83774 | raymond.hettinger | 2010-08-07 05:19:49 +0100 (Sat, 07 Aug 2010) | 1 line Fix markup ........ r83775 | raymond.hettinger | 2010-08-07 06:36:53 +0100 (Sat, 07 Aug 2010) | 1 line Add partition recipe to itertools docs. ........ r83776 | raymond.hettinger | 2010-08-07 06:54:08 +0100 (Sat, 07 Aug 2010) | 1 line Fix nit (sentinel on lhs of comparison). ........ r83777 | raymond.hettinger | 2010-08-07 08:36:55 +0100 (Sat, 07 Aug 2010) | 1 line Improve the docs for bisect to cover common searching tasks. ........ r83778 | victor.stinner | 2010-08-07 11:09:35 +0100 (Sat, 07 Aug 2010) | 1 line Issue #9425: skip tests if a filename is not encodable ........ r83779 | victor.stinner | 2010-08-07 11:57:17 +0100 (Sat, 07 Aug 2010) | 3 lines Issue #9425: Create run_command() subfunction Use PyUnicode_AsUTF8String() instead of _PyUnicode_AsString() ........ r83780 | mark.dickinson | 2010-08-07 13:33:36 +0100 (Sat, 07 Aug 2010) | 1 line Issue #8433: Fix test_curses failure for platforms with recent versions of ncurses. ........ r83783 | victor.stinner | 2010-08-07 17:34:25 +0100 (Sat, 07 Aug 2010) | 5 lines Issue #9425: Create run_file() subfunction * Call Py_MakePendingCalls() before converting the filename from wchar_t* to char* * Use PyUnicode_AsUTF8String() instead of _PyUnicode_AsString() ........ r83784 | raymond.hettinger | 2010-08-07 22:31:55 +0100 (Sat, 07 Aug 2010) | 1 line Clean-up docstring in examples. ........ r83785 | benjamin.peterson | 2010-08-07 22:32:12 +0100 (Sat, 07 Aug 2010) | 1 line kill outdated comment ........ r83792 | raymond.hettinger | 2010-08-08 00:31:27 +0100 (Sun, 08 Aug 2010) | 1 line Document implementation notes for priority queues ........ r83795 | raymond.hettinger | 2010-08-08 00:37:37 +0100 (Sun, 08 Aug 2010) | 1 line Fix typo ........ r83802 | benjamin.peterson | 2010-08-08 01:04:26 +0100 (Sun, 08 Aug 2010) | 1 line set svn:eol-style ........ r83804 | raymond.hettinger | 2010-08-08 01:29:08 +0100 (Sun, 08 Aug 2010) | 4 lines Improve readability of collections docs by adding a summary table at the top and by list concrete classes before abstract base classes. ........ r83808 | raymond.hettinger | 2010-08-08 02:13:42 +0100 (Sun, 08 Aug 2010) | 4 lines Issue #9507: Named tuple repr will now automatically display the right name in a tuple subclass. ........ r83809 | raymond.hettinger | 2010-08-08 02:30:45 +0100 (Sun, 08 Aug 2010) | 1 line Issue #4570: Clean-up tutorial example ........ Added: python/branches/py3k-dtoa/Include/pytime.h - copied unchanged from r83809, /python/branches/py3k/Include/pytime.h python/branches/py3k-dtoa/Lib/datetime.py - copied unchanged from r83809, /python/branches/py3k/Lib/datetime.py python/branches/py3k-dtoa/Lib/test/cfgparser.2 - copied unchanged from r83809, /python/branches/py3k/Lib/test/cfgparser.2 python/branches/py3k-dtoa/Lib/test/cfgparser.3 - copied unchanged from r83809, /python/branches/py3k/Lib/test/cfgparser.3 python/branches/py3k-dtoa/Lib/test/data/README - copied unchanged from r83809, /python/branches/py3k/Lib/test/data/README python/branches/py3k-dtoa/Lib/test/datetimetester.py - copied unchanged from r83809, /python/branches/py3k/Lib/test/datetimetester.py python/branches/py3k-dtoa/Lib/test/mock_socket.py - copied unchanged from r83809, /python/branches/py3k/Lib/test/mock_socket.py python/branches/py3k-dtoa/Lib/test/sndhdrdata/ - copied from r83809, /python/branches/py3k/Lib/test/sndhdrdata/ python/branches/py3k-dtoa/Lib/test/symlink_support.py - copied unchanged from r83809, /python/branches/py3k/Lib/test/symlink_support.py python/branches/py3k-dtoa/Lib/test/test_rlcompleter.py - copied unchanged from r83809, /python/branches/py3k/Lib/test/test_rlcompleter.py python/branches/py3k-dtoa/Lib/test/test_sched.py - copied unchanged from r83809, /python/branches/py3k/Lib/test/test_sched.py python/branches/py3k-dtoa/Lib/test/test_smtpd.py - copied unchanged from r83809, /python/branches/py3k/Lib/test/test_smtpd.py python/branches/py3k-dtoa/Lib/test/test_sndhdr.py - copied unchanged from r83809, /python/branches/py3k/Lib/test/test_sndhdr.py python/branches/py3k-dtoa/Lib/test/test_sys_setprofile.py - copied unchanged from r83809, /python/branches/py3k/Lib/test/test_sys_setprofile.py python/branches/py3k-dtoa/Lib/test/test_sys_settrace.py - copied unchanged from r83809, /python/branches/py3k/Lib/test/test_sys_settrace.py python/branches/py3k-dtoa/Lib/test/test_trace.py - copied unchanged from r83809, /python/branches/py3k/Lib/test/test_trace.py python/branches/py3k-dtoa/Lib/tkinter/__main__.py - copied unchanged from r83809, /python/branches/py3k/Lib/tkinter/__main__.py python/branches/py3k-dtoa/Modules/_datetimemodule.c - copied unchanged from r83809, /python/branches/py3k/Modules/_datetimemodule.c python/branches/py3k-dtoa/Python/pytime.c - copied unchanged from r83809, /python/branches/py3k/Python/pytime.c Removed: python/branches/py3k-dtoa/Demo/pdist/ python/branches/py3k-dtoa/Demo/scripts/toaiff.py python/branches/py3k-dtoa/Lib/distutils/tests/test_ccompiler.py python/branches/py3k-dtoa/Lib/distutils/tests/test_emxccompiler.py python/branches/py3k-dtoa/Lib/pdb.doc python/branches/py3k-dtoa/Lib/test/test_SimpleHTTPServer.py python/branches/py3k-dtoa/Lib/test/test_profilehooks.py python/branches/py3k-dtoa/Modules/datetimemodule.c python/branches/py3k-dtoa/Tools/faqwiz/ python/branches/py3k-dtoa/Tools/scripts/checkappend.py python/branches/py3k-dtoa/Tools/scripts/classfix.py python/branches/py3k-dtoa/Tools/scripts/cvsfiles.py python/branches/py3k-dtoa/Tools/scripts/logmerge.py python/branches/py3k-dtoa/Tools/scripts/methfix.py python/branches/py3k-dtoa/Tools/scripts/setup.py python/branches/py3k-dtoa/Tools/scripts/xxci.py python/branches/py3k-dtoa/Tools/versioncheck/ Modified: python/branches/py3k-dtoa/ (props changed) python/branches/py3k-dtoa/Demo/README python/branches/py3k-dtoa/Demo/embed/Makefile python/branches/py3k-dtoa/Demo/embed/demo.c python/branches/py3k-dtoa/Demo/embed/loop.c python/branches/py3k-dtoa/Demo/scripts/README python/branches/py3k-dtoa/Demo/tkinter/guido/AttrDialog.py python/branches/py3k-dtoa/Demo/tkinter/guido/ManPage.py python/branches/py3k-dtoa/Demo/tkinter/guido/brownian2.py python/branches/py3k-dtoa/Demo/tkinter/guido/kill.py python/branches/py3k-dtoa/Demo/zlib/minigzip.py python/branches/py3k-dtoa/Demo/zlib/zlibdemo.py python/branches/py3k-dtoa/Doc/Makefile python/branches/py3k-dtoa/Doc/README.txt python/branches/py3k-dtoa/Doc/c-api/init.rst python/branches/py3k-dtoa/Doc/c-api/unicode.rst python/branches/py3k-dtoa/Doc/c-api/weakref.rst python/branches/py3k-dtoa/Doc/conf.py python/branches/py3k-dtoa/Doc/distutils/apiref.rst python/branches/py3k-dtoa/Doc/distutils/builtdist.rst python/branches/py3k-dtoa/Doc/distutils/commandref.rst python/branches/py3k-dtoa/Doc/distutils/examples.rst python/branches/py3k-dtoa/Doc/distutils/setupscript.rst python/branches/py3k-dtoa/Doc/distutils/sourcedist.rst python/branches/py3k-dtoa/Doc/distutils/uploading.rst python/branches/py3k-dtoa/Doc/documenting/building.rst (props changed) python/branches/py3k-dtoa/Doc/documenting/markup.rst python/branches/py3k-dtoa/Doc/extending/extending.rst python/branches/py3k-dtoa/Doc/faq/extending.rst python/branches/py3k-dtoa/Doc/glossary.rst python/branches/py3k-dtoa/Doc/howto/descriptor.rst (props changed) python/branches/py3k-dtoa/Doc/howto/doanddont.rst python/branches/py3k-dtoa/Doc/install/index.rst python/branches/py3k-dtoa/Doc/library/2to3.rst python/branches/py3k-dtoa/Doc/library/abc.rst python/branches/py3k-dtoa/Doc/library/argparse.rst python/branches/py3k-dtoa/Doc/library/ast.rst python/branches/py3k-dtoa/Doc/library/base64.rst python/branches/py3k-dtoa/Doc/library/bdb.rst python/branches/py3k-dtoa/Doc/library/binascii.rst python/branches/py3k-dtoa/Doc/library/bisect.rst python/branches/py3k-dtoa/Doc/library/cgi.rst python/branches/py3k-dtoa/Doc/library/cmath.rst python/branches/py3k-dtoa/Doc/library/cmd.rst python/branches/py3k-dtoa/Doc/library/collections.rst python/branches/py3k-dtoa/Doc/library/configparser.rst python/branches/py3k-dtoa/Doc/library/constants.rst python/branches/py3k-dtoa/Doc/library/contextlib.rst python/branches/py3k-dtoa/Doc/library/datetime.rst python/branches/py3k-dtoa/Doc/library/dbm.rst python/branches/py3k-dtoa/Doc/library/doctest.rst python/branches/py3k-dtoa/Doc/library/email.errors.rst python/branches/py3k-dtoa/Doc/library/fileinput.rst python/branches/py3k-dtoa/Doc/library/fnmatch.rst python/branches/py3k-dtoa/Doc/library/ftplib.rst python/branches/py3k-dtoa/Doc/library/functions.rst python/branches/py3k-dtoa/Doc/library/functools.rst python/branches/py3k-dtoa/Doc/library/heapq.rst python/branches/py3k-dtoa/Doc/library/html.parser.rst python/branches/py3k-dtoa/Doc/library/http.client.rst python/branches/py3k-dtoa/Doc/library/http.cookiejar.rst python/branches/py3k-dtoa/Doc/library/importlib.rst python/branches/py3k-dtoa/Doc/library/io.rst python/branches/py3k-dtoa/Doc/library/itertools.rst python/branches/py3k-dtoa/Doc/library/linecache.rst python/branches/py3k-dtoa/Doc/library/locale.rst python/branches/py3k-dtoa/Doc/library/math.rst python/branches/py3k-dtoa/Doc/library/mmap.rst python/branches/py3k-dtoa/Doc/library/multiprocessing.rst python/branches/py3k-dtoa/Doc/library/optparse.rst python/branches/py3k-dtoa/Doc/library/os.path.rst python/branches/py3k-dtoa/Doc/library/os.rst python/branches/py3k-dtoa/Doc/library/parser.rst python/branches/py3k-dtoa/Doc/library/pdb.rst python/branches/py3k-dtoa/Doc/library/pickletools.rst python/branches/py3k-dtoa/Doc/library/pyexpat.rst python/branches/py3k-dtoa/Doc/library/re.rst python/branches/py3k-dtoa/Doc/library/select.rst python/branches/py3k-dtoa/Doc/library/signal.rst python/branches/py3k-dtoa/Doc/library/smtpd.rst python/branches/py3k-dtoa/Doc/library/smtplib.rst python/branches/py3k-dtoa/Doc/library/socket.rst python/branches/py3k-dtoa/Doc/library/sqlite3.rst python/branches/py3k-dtoa/Doc/library/stdtypes.rst python/branches/py3k-dtoa/Doc/library/string.rst python/branches/py3k-dtoa/Doc/library/struct.rst python/branches/py3k-dtoa/Doc/library/subprocess.rst python/branches/py3k-dtoa/Doc/library/sys.rst python/branches/py3k-dtoa/Doc/library/test.rst python/branches/py3k-dtoa/Doc/library/threading.rst python/branches/py3k-dtoa/Doc/library/tkinter.rst python/branches/py3k-dtoa/Doc/library/unittest.rst python/branches/py3k-dtoa/Doc/library/urllib.parse.rst python/branches/py3k-dtoa/Doc/library/urllib.request.rst python/branches/py3k-dtoa/Doc/library/wsgiref.rst python/branches/py3k-dtoa/Doc/library/xml.dom.minidom.rst python/branches/py3k-dtoa/Doc/library/xml.dom.rst python/branches/py3k-dtoa/Doc/library/xml.sax.reader.rst python/branches/py3k-dtoa/Doc/library/zipfile.rst python/branches/py3k-dtoa/Doc/license.rst python/branches/py3k-dtoa/Doc/make.bat python/branches/py3k-dtoa/Doc/reference/compound_stmts.rst python/branches/py3k-dtoa/Doc/reference/expressions.rst python/branches/py3k-dtoa/Doc/reference/lexical_analysis.rst python/branches/py3k-dtoa/Doc/tools/sphinx-build.py python/branches/py3k-dtoa/Doc/tools/sphinxext/indexsidebar.html python/branches/py3k-dtoa/Doc/tools/sphinxext/pyspecific.py python/branches/py3k-dtoa/Doc/tutorial/classes.rst python/branches/py3k-dtoa/Doc/tutorial/datastructures.rst python/branches/py3k-dtoa/Doc/tutorial/floatingpoint.rst python/branches/py3k-dtoa/Doc/using/cmdline.rst python/branches/py3k-dtoa/Doc/whatsnew/2.0.rst python/branches/py3k-dtoa/Doc/whatsnew/2.4.rst python/branches/py3k-dtoa/Doc/whatsnew/2.5.rst python/branches/py3k-dtoa/Doc/whatsnew/2.7.rst python/branches/py3k-dtoa/Doc/whatsnew/3.2.rst python/branches/py3k-dtoa/Grammar/Grammar python/branches/py3k-dtoa/Include/Python.h python/branches/py3k-dtoa/Include/floatobject.h python/branches/py3k-dtoa/Include/patchlevel.h python/branches/py3k-dtoa/Include/structseq.h python/branches/py3k-dtoa/LICENSE python/branches/py3k-dtoa/Lib/_abcoll.py python/branches/py3k-dtoa/Lib/_strptime.py python/branches/py3k-dtoa/Lib/argparse.py python/branches/py3k-dtoa/Lib/ast.py python/branches/py3k-dtoa/Lib/asyncore.py python/branches/py3k-dtoa/Lib/base64.py python/branches/py3k-dtoa/Lib/bdb.py python/branches/py3k-dtoa/Lib/cProfile.py python/branches/py3k-dtoa/Lib/cmd.py python/branches/py3k-dtoa/Lib/collections.py python/branches/py3k-dtoa/Lib/configparser.py python/branches/py3k-dtoa/Lib/ctypes/__init__.py python/branches/py3k-dtoa/Lib/ctypes/test/test_arrays.py python/branches/py3k-dtoa/Lib/ctypes/test/test_bitfields.py python/branches/py3k-dtoa/Lib/ctypes/test/test_buffers.py python/branches/py3k-dtoa/Lib/ctypes/test/test_bytes.py python/branches/py3k-dtoa/Lib/ctypes/test/test_callbacks.py python/branches/py3k-dtoa/Lib/ctypes/test/test_cast.py python/branches/py3k-dtoa/Lib/ctypes/test/test_cfuncs.py python/branches/py3k-dtoa/Lib/ctypes/test/test_errno.py python/branches/py3k-dtoa/Lib/ctypes/test/test_internals.py python/branches/py3k-dtoa/Lib/ctypes/test/test_keeprefs.py python/branches/py3k-dtoa/Lib/ctypes/test/test_libc.py python/branches/py3k-dtoa/Lib/ctypes/test/test_loading.py python/branches/py3k-dtoa/Lib/ctypes/test/test_objects.py python/branches/py3k-dtoa/Lib/ctypes/test/test_parameters.py python/branches/py3k-dtoa/Lib/ctypes/test/test_prototypes.py python/branches/py3k-dtoa/Lib/ctypes/test/test_python_api.py python/branches/py3k-dtoa/Lib/ctypes/test/test_random_things.py python/branches/py3k-dtoa/Lib/ctypes/test/test_repr.py python/branches/py3k-dtoa/Lib/ctypes/test/test_returnfuncptrs.py python/branches/py3k-dtoa/Lib/ctypes/test/test_stringptr.py python/branches/py3k-dtoa/Lib/ctypes/test/test_strings.py python/branches/py3k-dtoa/Lib/ctypes/test/test_structures.py python/branches/py3k-dtoa/Lib/ctypes/test/test_unicode.py python/branches/py3k-dtoa/Lib/curses/wrapper.py python/branches/py3k-dtoa/Lib/dbm/__init__.py python/branches/py3k-dtoa/Lib/decimal.py python/branches/py3k-dtoa/Lib/distutils/__init__.py python/branches/py3k-dtoa/Lib/distutils/archive_util.py python/branches/py3k-dtoa/Lib/distutils/bcppcompiler.py python/branches/py3k-dtoa/Lib/distutils/ccompiler.py python/branches/py3k-dtoa/Lib/distutils/cmd.py python/branches/py3k-dtoa/Lib/distutils/command/bdist.py python/branches/py3k-dtoa/Lib/distutils/command/bdist_dumb.py python/branches/py3k-dtoa/Lib/distutils/command/bdist_msi.py python/branches/py3k-dtoa/Lib/distutils/command/bdist_rpm.py python/branches/py3k-dtoa/Lib/distutils/command/bdist_wininst.py python/branches/py3k-dtoa/Lib/distutils/command/build.py python/branches/py3k-dtoa/Lib/distutils/command/build_clib.py python/branches/py3k-dtoa/Lib/distutils/command/build_ext.py python/branches/py3k-dtoa/Lib/distutils/command/build_py.py python/branches/py3k-dtoa/Lib/distutils/command/build_scripts.py python/branches/py3k-dtoa/Lib/distutils/command/config.py python/branches/py3k-dtoa/Lib/distutils/command/install.py python/branches/py3k-dtoa/Lib/distutils/command/register.py python/branches/py3k-dtoa/Lib/distutils/command/sdist.py python/branches/py3k-dtoa/Lib/distutils/command/upload.py python/branches/py3k-dtoa/Lib/distutils/config.py python/branches/py3k-dtoa/Lib/distutils/core.py python/branches/py3k-dtoa/Lib/distutils/cygwinccompiler.py python/branches/py3k-dtoa/Lib/distutils/dep_util.py python/branches/py3k-dtoa/Lib/distutils/dir_util.py python/branches/py3k-dtoa/Lib/distutils/dist.py python/branches/py3k-dtoa/Lib/distutils/emxccompiler.py python/branches/py3k-dtoa/Lib/distutils/errors.py python/branches/py3k-dtoa/Lib/distutils/extension.py python/branches/py3k-dtoa/Lib/distutils/fancy_getopt.py python/branches/py3k-dtoa/Lib/distutils/file_util.py python/branches/py3k-dtoa/Lib/distutils/filelist.py python/branches/py3k-dtoa/Lib/distutils/msvc9compiler.py python/branches/py3k-dtoa/Lib/distutils/msvccompiler.py python/branches/py3k-dtoa/Lib/distutils/sysconfig.py python/branches/py3k-dtoa/Lib/distutils/tests/support.py python/branches/py3k-dtoa/Lib/distutils/tests/test_archive_util.py python/branches/py3k-dtoa/Lib/distutils/tests/test_bdist.py python/branches/py3k-dtoa/Lib/distutils/tests/test_bdist_dumb.py python/branches/py3k-dtoa/Lib/distutils/tests/test_bdist_rpm.py python/branches/py3k-dtoa/Lib/distutils/tests/test_bdist_wininst.py python/branches/py3k-dtoa/Lib/distutils/tests/test_build_clib.py python/branches/py3k-dtoa/Lib/distutils/tests/test_build_ext.py python/branches/py3k-dtoa/Lib/distutils/tests/test_build_py.py python/branches/py3k-dtoa/Lib/distutils/tests/test_build_scripts.py python/branches/py3k-dtoa/Lib/distutils/tests/test_cmd.py python/branches/py3k-dtoa/Lib/distutils/tests/test_cygwinccompiler.py python/branches/py3k-dtoa/Lib/distutils/tests/test_dist.py python/branches/py3k-dtoa/Lib/distutils/tests/test_extension.py python/branches/py3k-dtoa/Lib/distutils/tests/test_file_util.py python/branches/py3k-dtoa/Lib/distutils/tests/test_filelist.py python/branches/py3k-dtoa/Lib/distutils/tests/test_install.py python/branches/py3k-dtoa/Lib/distutils/tests/test_install_lib.py python/branches/py3k-dtoa/Lib/distutils/tests/test_log.py (props changed) python/branches/py3k-dtoa/Lib/distutils/tests/test_register.py python/branches/py3k-dtoa/Lib/distutils/tests/test_sdist.py python/branches/py3k-dtoa/Lib/distutils/tests/test_sysconfig.py python/branches/py3k-dtoa/Lib/distutils/tests/test_unixccompiler.py python/branches/py3k-dtoa/Lib/distutils/tests/test_upload.py python/branches/py3k-dtoa/Lib/distutils/tests/test_util.py python/branches/py3k-dtoa/Lib/distutils/text_file.py python/branches/py3k-dtoa/Lib/distutils/unixccompiler.py python/branches/py3k-dtoa/Lib/distutils/util.py python/branches/py3k-dtoa/Lib/doctest.py python/branches/py3k-dtoa/Lib/email/base64mime.py python/branches/py3k-dtoa/Lib/email/feedparser.py python/branches/py3k-dtoa/Lib/email/header.py python/branches/py3k-dtoa/Lib/email/message.py python/branches/py3k-dtoa/Lib/email/test/test_email.py python/branches/py3k-dtoa/Lib/fileinput.py python/branches/py3k-dtoa/Lib/fnmatch.py python/branches/py3k-dtoa/Lib/functools.py python/branches/py3k-dtoa/Lib/getopt.py python/branches/py3k-dtoa/Lib/http/client.py python/branches/py3k-dtoa/Lib/http/cookiejar.py python/branches/py3k-dtoa/Lib/http/cookies.py python/branches/py3k-dtoa/Lib/http/server.py python/branches/py3k-dtoa/Lib/idlelib/NEWS.txt python/branches/py3k-dtoa/Lib/idlelib/PyShell.py python/branches/py3k-dtoa/Lib/idlelib/idlever.py python/branches/py3k-dtoa/Lib/imaplib.py python/branches/py3k-dtoa/Lib/importlib/test/benchmark.py python/branches/py3k-dtoa/Lib/json/__init__.py python/branches/py3k-dtoa/Lib/json/decoder.py python/branches/py3k-dtoa/Lib/json/encoder.py python/branches/py3k-dtoa/Lib/json/tests/test_fail.py python/branches/py3k-dtoa/Lib/mailbox.py (props changed) python/branches/py3k-dtoa/Lib/multiprocessing/connection.py python/branches/py3k-dtoa/Lib/multiprocessing/forking.py python/branches/py3k-dtoa/Lib/multiprocessing/heap.py python/branches/py3k-dtoa/Lib/multiprocessing/reduction.py python/branches/py3k-dtoa/Lib/ntpath.py python/branches/py3k-dtoa/Lib/os.py python/branches/py3k-dtoa/Lib/pdb.py python/branches/py3k-dtoa/Lib/pickle.py python/branches/py3k-dtoa/Lib/pickletools.py python/branches/py3k-dtoa/Lib/platform.py python/branches/py3k-dtoa/Lib/posixpath.py python/branches/py3k-dtoa/Lib/profile.py python/branches/py3k-dtoa/Lib/pstats.py python/branches/py3k-dtoa/Lib/pydoc.py python/branches/py3k-dtoa/Lib/pydoc_data/topics.py python/branches/py3k-dtoa/Lib/re.py python/branches/py3k-dtoa/Lib/site.py python/branches/py3k-dtoa/Lib/smtpd.py python/branches/py3k-dtoa/Lib/smtplib.py python/branches/py3k-dtoa/Lib/sndhdr.py python/branches/py3k-dtoa/Lib/socket.py python/branches/py3k-dtoa/Lib/string.py python/branches/py3k-dtoa/Lib/subprocess.py python/branches/py3k-dtoa/Lib/sysconfig.py python/branches/py3k-dtoa/Lib/tarfile.py python/branches/py3k-dtoa/Lib/test/decimaltestdata/extra.decTest python/branches/py3k-dtoa/Lib/test/formatfloat_testcases.txt python/branches/py3k-dtoa/Lib/test/math_testcases.txt python/branches/py3k-dtoa/Lib/test/pickletester.py python/branches/py3k-dtoa/Lib/test/regrtest.py python/branches/py3k-dtoa/Lib/test/script_helper.py python/branches/py3k-dtoa/Lib/test/sortperf.py python/branches/py3k-dtoa/Lib/test/support.py python/branches/py3k-dtoa/Lib/test/test_argparse.py python/branches/py3k-dtoa/Lib/test/test_array.py python/branches/py3k-dtoa/Lib/test/test_ast.py python/branches/py3k-dtoa/Lib/test/test_asyncore.py python/branches/py3k-dtoa/Lib/test/test_binascii.py python/branches/py3k-dtoa/Lib/test/test_binop.py python/branches/py3k-dtoa/Lib/test/test_bz2.py python/branches/py3k-dtoa/Lib/test/test_calendar.py python/branches/py3k-dtoa/Lib/test/test_cfgparser.py python/branches/py3k-dtoa/Lib/test/test_cmath.py python/branches/py3k-dtoa/Lib/test/test_cmd.py python/branches/py3k-dtoa/Lib/test/test_collections.py python/branches/py3k-dtoa/Lib/test/test_complex.py python/branches/py3k-dtoa/Lib/test/test_curses.py python/branches/py3k-dtoa/Lib/test/test_datetime.py python/branches/py3k-dtoa/Lib/test/test_decimal.py python/branches/py3k-dtoa/Lib/test/test_doctest.py python/branches/py3k-dtoa/Lib/test/test_fileinput.py python/branches/py3k-dtoa/Lib/test/test_float.py python/branches/py3k-dtoa/Lib/test/test_fnmatch.py python/branches/py3k-dtoa/Lib/test/test_functools.py python/branches/py3k-dtoa/Lib/test/test_gdb.py python/branches/py3k-dtoa/Lib/test/test_getopt.py python/branches/py3k-dtoa/Lib/test/test_glob.py python/branches/py3k-dtoa/Lib/test/test_http_cookiejar.py python/branches/py3k-dtoa/Lib/test/test_http_cookies.py python/branches/py3k-dtoa/Lib/test/test_httplib.py python/branches/py3k-dtoa/Lib/test/test_httpservers.py python/branches/py3k-dtoa/Lib/test/test_import.py python/branches/py3k-dtoa/Lib/test/test_math.py python/branches/py3k-dtoa/Lib/test/test_minidom.py python/branches/py3k-dtoa/Lib/test/test_mmap.py python/branches/py3k-dtoa/Lib/test/test_ntpath.py python/branches/py3k-dtoa/Lib/test/test_numeric_tower.py (props changed) python/branches/py3k-dtoa/Lib/test/test_optparse.py python/branches/py3k-dtoa/Lib/test/test_os.py python/branches/py3k-dtoa/Lib/test/test_osx_env.py python/branches/py3k-dtoa/Lib/test/test_pdb.py python/branches/py3k-dtoa/Lib/test/test_platform.py python/branches/py3k-dtoa/Lib/test/test_posix.py python/branches/py3k-dtoa/Lib/test/test_posixpath.py python/branches/py3k-dtoa/Lib/test/test_pow.py python/branches/py3k-dtoa/Lib/test/test_pstats.py python/branches/py3k-dtoa/Lib/test/test_queue.py python/branches/py3k-dtoa/Lib/test/test_re.py python/branches/py3k-dtoa/Lib/test/test_robotparser.py python/branches/py3k-dtoa/Lib/test/test_sax.py python/branches/py3k-dtoa/Lib/test/test_set.py python/branches/py3k-dtoa/Lib/test/test_shutil.py python/branches/py3k-dtoa/Lib/test/test_signal.py python/branches/py3k-dtoa/Lib/test/test_site.py python/branches/py3k-dtoa/Lib/test/test_smtplib.py python/branches/py3k-dtoa/Lib/test/test_ssl.py python/branches/py3k-dtoa/Lib/test/test_struct.py python/branches/py3k-dtoa/Lib/test/test_structseq.py python/branches/py3k-dtoa/Lib/test/test_subprocess.py python/branches/py3k-dtoa/Lib/test/test_sunau.py (props changed) python/branches/py3k-dtoa/Lib/test/test_sundry.py python/branches/py3k-dtoa/Lib/test/test_sys.py python/branches/py3k-dtoa/Lib/test/test_sysconfig.py python/branches/py3k-dtoa/Lib/test/test_tarfile.py python/branches/py3k-dtoa/Lib/test/test_tcl.py python/branches/py3k-dtoa/Lib/test/test_threaded_import.py python/branches/py3k-dtoa/Lib/test/test_threading.py python/branches/py3k-dtoa/Lib/test/test_threading_local.py python/branches/py3k-dtoa/Lib/test/test_tokenize.py python/branches/py3k-dtoa/Lib/test/test_unicode.py python/branches/py3k-dtoa/Lib/test/test_unicodedata.py python/branches/py3k-dtoa/Lib/test/test_unpack.py python/branches/py3k-dtoa/Lib/test/test_urllib.py python/branches/py3k-dtoa/Lib/test/test_urllib2.py python/branches/py3k-dtoa/Lib/test/test_urlparse.py python/branches/py3k-dtoa/Lib/test/test_xml_etree.py python/branches/py3k-dtoa/Lib/threading.py python/branches/py3k-dtoa/Lib/timeit.py python/branches/py3k-dtoa/Lib/trace.py python/branches/py3k-dtoa/Lib/unittest/case.py python/branches/py3k-dtoa/Lib/unittest/test/test_case.py python/branches/py3k-dtoa/Lib/urllib/parse.py python/branches/py3k-dtoa/Lib/urllib/request.py python/branches/py3k-dtoa/Lib/urllib/robotparser.py python/branches/py3k-dtoa/Lib/xml/dom/expatbuilder.py python/branches/py3k-dtoa/Lib/xml/dom/minidom.py python/branches/py3k-dtoa/Lib/xmlrpc/client.py python/branches/py3k-dtoa/Makefile.pre.in python/branches/py3k-dtoa/Misc/ACKS python/branches/py3k-dtoa/Misc/NEWS python/branches/py3k-dtoa/Misc/RPM/python-3.2.spec python/branches/py3k-dtoa/Misc/developers.txt python/branches/py3k-dtoa/Misc/maintainers.rst python/branches/py3k-dtoa/Misc/python-wing.wpr python/branches/py3k-dtoa/Modules/Setup.dist python/branches/py3k-dtoa/Modules/_ctypes/_ctypes.c python/branches/py3k-dtoa/Modules/_ctypes/callproc.c python/branches/py3k-dtoa/Modules/_ctypes/cfield.c python/branches/py3k-dtoa/Modules/_ctypes/ctypes.h python/branches/py3k-dtoa/Modules/_cursesmodule.c python/branches/py3k-dtoa/Modules/_io/_iomodule.c python/branches/py3k-dtoa/Modules/_io/bufferedio.c python/branches/py3k-dtoa/Modules/_json.c python/branches/py3k-dtoa/Modules/_pickle.c python/branches/py3k-dtoa/Modules/_ssl.c python/branches/py3k-dtoa/Modules/_struct.c python/branches/py3k-dtoa/Modules/_time.c (props changed) python/branches/py3k-dtoa/Modules/_time.h (props changed) python/branches/py3k-dtoa/Modules/arraymodule.c python/branches/py3k-dtoa/Modules/binascii.c python/branches/py3k-dtoa/Modules/bz2module.c python/branches/py3k-dtoa/Modules/cmathmodule.c python/branches/py3k-dtoa/Modules/getpath.c python/branches/py3k-dtoa/Modules/main.c python/branches/py3k-dtoa/Modules/makesetup python/branches/py3k-dtoa/Modules/mathmodule.c python/branches/py3k-dtoa/Modules/mmapmodule.c python/branches/py3k-dtoa/Modules/posixmodule.c python/branches/py3k-dtoa/Modules/readline.c python/branches/py3k-dtoa/Modules/selectmodule.c python/branches/py3k-dtoa/Modules/sha1module.c python/branches/py3k-dtoa/Modules/signalmodule.c python/branches/py3k-dtoa/Modules/timemodule.c python/branches/py3k-dtoa/Modules/zipimport.c python/branches/py3k-dtoa/Modules/zlib/ChangeLog python/branches/py3k-dtoa/Modules/zlib/FAQ python/branches/py3k-dtoa/Modules/zlib/INDEX python/branches/py3k-dtoa/Modules/zlib/Makefile python/branches/py3k-dtoa/Modules/zlib/Makefile.in python/branches/py3k-dtoa/Modules/zlib/README python/branches/py3k-dtoa/Modules/zlib/adler32.c python/branches/py3k-dtoa/Modules/zlib/compress.c python/branches/py3k-dtoa/Modules/zlib/configure python/branches/py3k-dtoa/Modules/zlib/crc32.c python/branches/py3k-dtoa/Modules/zlib/deflate.c python/branches/py3k-dtoa/Modules/zlib/deflate.h python/branches/py3k-dtoa/Modules/zlib/example.c python/branches/py3k-dtoa/Modules/zlib/infback.c python/branches/py3k-dtoa/Modules/zlib/inffast.c python/branches/py3k-dtoa/Modules/zlib/inffast.h python/branches/py3k-dtoa/Modules/zlib/inflate.c python/branches/py3k-dtoa/Modules/zlib/inflate.h python/branches/py3k-dtoa/Modules/zlib/inftrees.c python/branches/py3k-dtoa/Modules/zlib/inftrees.h python/branches/py3k-dtoa/Modules/zlib/make_vms.com python/branches/py3k-dtoa/Modules/zlib/minigzip.c python/branches/py3k-dtoa/Modules/zlib/trees.c python/branches/py3k-dtoa/Modules/zlib/trees.h python/branches/py3k-dtoa/Modules/zlib/uncompr.c python/branches/py3k-dtoa/Modules/zlib/zconf.h python/branches/py3k-dtoa/Modules/zlib/zlib.3 python/branches/py3k-dtoa/Modules/zlib/zlib.h python/branches/py3k-dtoa/Modules/zlib/zutil.c python/branches/py3k-dtoa/Modules/zlib/zutil.h python/branches/py3k-dtoa/Objects/bytearrayobject.c python/branches/py3k-dtoa/Objects/complexobject.c python/branches/py3k-dtoa/Objects/floatobject.c python/branches/py3k-dtoa/Objects/iterobject.c python/branches/py3k-dtoa/Objects/memoryobject.c python/branches/py3k-dtoa/Objects/object.c python/branches/py3k-dtoa/Objects/setobject.c python/branches/py3k-dtoa/Objects/sliceobject.c python/branches/py3k-dtoa/Objects/stringlib/formatter.h python/branches/py3k-dtoa/Objects/structseq.c python/branches/py3k-dtoa/Objects/unicodeobject.c python/branches/py3k-dtoa/PC/VS8.0/pythoncore.vcproj python/branches/py3k-dtoa/PC/_subprocess.c python/branches/py3k-dtoa/PC/config.c python/branches/py3k-dtoa/PC/python_nt.rc python/branches/py3k-dtoa/PCbuild/build_ssl.py python/branches/py3k-dtoa/PCbuild/pyproject.vsprops python/branches/py3k-dtoa/PCbuild/pythoncore.vcproj python/branches/py3k-dtoa/PCbuild/readme.txt python/branches/py3k-dtoa/Python/ceval.c python/branches/py3k-dtoa/Python/getargs.c python/branches/py3k-dtoa/Python/getcopyright.c python/branches/py3k-dtoa/Python/getversion.c python/branches/py3k-dtoa/Python/graminit.c python/branches/py3k-dtoa/Python/pythonrun.c python/branches/py3k-dtoa/README python/branches/py3k-dtoa/Tools/README python/branches/py3k-dtoa/Tools/buildbot/external-common.bat python/branches/py3k-dtoa/Tools/framer/framer/bases.py python/branches/py3k-dtoa/Tools/framer/framer/template.py python/branches/py3k-dtoa/Tools/freeze/freeze.py python/branches/py3k-dtoa/Tools/freeze/makeconfig.py python/branches/py3k-dtoa/Tools/gdb/libpython.py python/branches/py3k-dtoa/Tools/i18n/msgfmt.py python/branches/py3k-dtoa/Tools/msi/msi.py python/branches/py3k-dtoa/Tools/msi/msilib.py python/branches/py3k-dtoa/Tools/msi/uuids.py python/branches/py3k-dtoa/Tools/pynche/ChipViewer.py python/branches/py3k-dtoa/Tools/pynche/DetailsViewer.py python/branches/py3k-dtoa/Tools/pynche/ListViewer.py python/branches/py3k-dtoa/Tools/pynche/PyncheWidget.py python/branches/py3k-dtoa/Tools/pynche/StripViewer.py python/branches/py3k-dtoa/Tools/pynche/Switchboard.py python/branches/py3k-dtoa/Tools/pynche/TextViewer.py python/branches/py3k-dtoa/Tools/pynche/TypeinViewer.py python/branches/py3k-dtoa/Tools/scripts/README python/branches/py3k-dtoa/Tools/scripts/byext.py python/branches/py3k-dtoa/Tools/scripts/serve.py python/branches/py3k-dtoa/Tools/scripts/win_add2path.py python/branches/py3k-dtoa/configure python/branches/py3k-dtoa/configure.in python/branches/py3k-dtoa/setup.py Modified: python/branches/py3k-dtoa/Demo/README ============================================================================== --- python/branches/py3k-dtoa/Demo/README (original) +++ python/branches/py3k-dtoa/Demo/README Sun Aug 8 12:41:24 2010 @@ -13,49 +13,50 @@ in case I change my mind about them. -cgi CGI examples (see also ../Tools/faqwiz/.) +cgi CGI examples. -classes Some examples of how to use classes. +classes Some examples of how to use classes. -comparisons A set of responses to a really old language-comparison - challenge. +comparisons A set of responses to a really old language-comparison + challenge. -curses A set of curses demos. +curses A set of curses demos. -embed An example of embedding Python in another application - (see also pysvr). +distutils Test for using transparent 2to3 conversion in distutils. -imputil Demonstration subclasses of imputil.Importer. +embed An example of embedding Python in another application + (see also pysvr). -md5test Test program for the optional md5 module. +imputil Demonstration subclasses of imputil.Importer. -metaclasses The code from the 1.5 metaclasses paper on the web. +md5test Test program for the optional md5 module. -parser Example using the parser module. +newmetaclasses Demonstration of metaclasses. -pdist Old, unfinished code messing with CVS, RCS and remote - files. +parser Example using the parser module. -pysvr An example of embedding Python in a threaded - application. +pysvr An example of embedding Python in a threaded + application. -rpc A set of classes for building clients and servers for - Sun RPC. +rpc A set of classes for building clients and servers for + Sun RPC. -scripts Some useful Python scripts that I put in my bin - directory. No optional built-in modules needed. +scripts Some useful Python scripts that I put in my bin + directory. No optional built-in modules needed. -sockets Examples for the new built-in module 'socket'. +sockets Examples for the new built-in module 'socket'. -threads Demos that use the 'thread' module. (Currently these - only run on SGIs, but this may change in the future.) +threads Demos that use the 'thread' module. (Currently these + only run on SGIs, but this may change in the future.) -tix Demos using the Tix widget set addition to Tkinter. +tix Demos using the Tix widget set addition to Tkinter. -tkinter Demos using the Tk interface (including Matt Conway's - excellent set of demos). +tkinter Demos using the Tk interface (including Matt Conway's + excellent set of demos). -xml Some XML demos. +turtle Demos for the "turtle" module. -zlib Some demos for the zlib module (see also the standard - library module gzip.py). +xml Some XML demos. + +zlib Some demos for the zlib module (see also the standard + library module gzip.py). Modified: python/branches/py3k-dtoa/Demo/embed/Makefile ============================================================================== --- python/branches/py3k-dtoa/Demo/embed/Makefile (original) +++ python/branches/py3k-dtoa/Demo/embed/Makefile Sun Aug 8 12:41:24 2010 @@ -22,7 +22,7 @@ LIBPYTHON= $(blddir)/libpython$(VERSION).a # XXX edit LIBS (in particular) to match $(blddir)/Makefile -LIBS= -lnsl -ldl -lreadline -ltermcap -lieee -lpthread -lutil +LIBS= -lnsl -ldl -lreadline -lieee -lpthread -lutil LDFLAGS= -Xlinker -export-dynamic SYSLIBS= -lm MODLIBS= Modified: python/branches/py3k-dtoa/Demo/embed/demo.c ============================================================================== --- python/branches/py3k-dtoa/Demo/embed/demo.c (original) +++ python/branches/py3k-dtoa/Demo/embed/demo.c Sun Aug 8 12:41:24 2010 @@ -22,7 +22,7 @@ /* Define sys.argv. It is up to the application if you want this; you can also leave it undefined (since the Python code is generally not a main program it has no business - touching sys.argv...) + touching sys.argv...) If the third argument is true, sys.path is modified to include either the directory containing the script named by argv[0], or @@ -32,7 +32,7 @@ directory (say, a file named os.py) that your application would then import and run. */ - PySys_SetArgvEx(argc, argv, 0); + PySys_SetArgvEx(2, args, 0); /* Do some application specific code */ printf("Hello, brave new world\n\n"); Modified: python/branches/py3k-dtoa/Demo/embed/loop.c ============================================================================== --- python/branches/py3k-dtoa/Demo/embed/loop.c (original) +++ python/branches/py3k-dtoa/Demo/embed/loop.c Sun Aug 8 12:41:24 2010 @@ -19,7 +19,7 @@ count = atoi(argv[2]); } - Py_SetProgramName(argv[0]); + Py_SetProgramName(L"loop"); /* uncomment this if you don't want to load site.py */ /* Py_NoSiteFlag = 1; */ Modified: python/branches/py3k-dtoa/Demo/scripts/README ============================================================================== --- python/branches/py3k-dtoa/Demo/scripts/README (original) +++ python/branches/py3k-dtoa/Demo/scripts/README Sun Aug 8 12:41:24 2010 @@ -2,21 +2,21 @@ See also the Tools/scripts directory! -beer.py Print the classic 'bottles of beer' list -eqfix.py Fix .py files to use the correct equality test operator -fact.py Factorize numbers -find-uname.py Search for Unicode characters using regexps -from.py Summarize mailbox -lpwatch.py Watch BSD line printer queues -makedir.py Like mkdir -p -markov.py Markov chain simulation of words or characters -mboxconvert.py Convert MH or MMDF mailboxes to unix mailbox format -morse.py Produce morse code (as an AIFF file) -newslist.py List all newsgroups on a NNTP server as HTML pages -pi.py Print all digits of pi -- given enough time and memory -pp.py Emulate some Perl command line options -primes.py Print prime numbers -queens.py Dijkstra's solution to Wirth's "N Queens problem" -script.py Equivalent to BSD script(1) -- by Steen Lumholt -unbirthday.py Print unbirthday count -update.py Update a bunch of files according to a script. +beer.py Print the classic 'bottles of beer' list +eqfix.py Fix .py files to use the correct equality test operator +fact.py Factorize numbers +find-uname.py Search for Unicode characters using regexps +from.py Summarize mailbox +lpwatch.py Watch BSD line printer queues +makedir.py Like mkdir -p +markov.py Markov chain simulation of words or characters +mboxconvert.py Convert MH or MMDF mailboxes to unix mailbox format +morse.py Produce morse code (as an AIFF file) +newslist.py List all newsgroups on a NNTP server as HTML pages +pi.py Print all digits of pi -- given enough time and memory +pp.py Emulate some Perl command line options +primes.py Print prime numbers +queens.py Dijkstra's solution to Wirth's "N Queens problem" +script.py Equivalent to BSD script(1) -- by Steen Lumholt +unbirthday.py Print unbirthday count +update.py Update a bunch of files according to a script Deleted: python/branches/py3k-dtoa/Demo/scripts/toaiff.py ============================================================================== --- python/branches/py3k-dtoa/Demo/scripts/toaiff.py Sun Aug 8 12:41:24 2010 +++ (empty file) @@ -1,107 +0,0 @@ -"""Convert "arbitrary" sound files to AIFF (Apple and SGI's audio format). - -Input may be compressed. -Uncompressed file type may be AIFF, WAV, VOC, 8SVX, NeXT/Sun, and others. -An exception is raised if the file is not of a recognized type. -Returned filename is either the input filename or a temporary filename; -in the latter case the caller must ensure that it is removed. -Other temporary files used are removed by the function. -""" - -import os -import tempfile -import pipes -import sndhdr - -__all__ = ["error", "toaiff"] - -table = {} - -t = pipes.Template() -t.append('sox -t au - -t aiff -r 8000 -', '--') -table['au'] = t - -# XXX The following is actually sub-optimal. -# XXX The HCOM sampling rate can be 22k, 22k/2, 22k/3 or 22k/4. -# XXX We must force the output sampling rate else the SGI won't play -# XXX files sampled at 5.5k or 7.333k; however this means that files -# XXX sampled at 11k are unnecessarily expanded. -# XXX Similar comments apply to some other file types. -t = pipes.Template() -t.append('sox -t hcom - -t aiff -r 22050 -', '--') -table['hcom'] = t - -t = pipes.Template() -t.append('sox -t voc - -t aiff -r 11025 -', '--') -table['voc'] = t - -t = pipes.Template() -t.append('sox -t wav - -t aiff -', '--') -table['wav'] = t - -t = pipes.Template() -t.append('sox -t 8svx - -t aiff -r 16000 -', '--') -table['8svx'] = t - -t = pipes.Template() -t.append('sox -t sndt - -t aiff -r 16000 -', '--') -table['sndt'] = t - -t = pipes.Template() -t.append('sox -t sndr - -t aiff -r 16000 -', '--') -table['sndr'] = t - -uncompress = pipes.Template() -uncompress.append('uncompress', '--') - - -class error(Exception): - pass - -def toaiff(filename): - temps = [] - ret = None - try: - ret = _toaiff(filename, temps) - finally: - for temp in temps[:]: - if temp != ret: - try: - os.unlink(temp) - except os.error: - pass - temps.remove(temp) - return ret - -def _toaiff(filename, temps): - if filename[-2:] == '.Z': - (fd, fname) = tempfile.mkstemp() - os.close(fd) - temps.append(fname) - sts = uncompress.copy(filename, fname) - if sts: - raise error(filename + ': uncompress failed') - else: - fname = filename - try: - ftype = sndhdr.whathdr(fname) - if ftype: - ftype = ftype[0] # All we're interested in - except IOError as msg: - if type(msg) == type(()) and len(msg) == 2 and \ - type(msg.args[0]) == type(0) and type(msg.args[1]) == type(''): - msg = msg.args[1] - if type(msg) != type(''): - msg = repr(msg) - raise error(filename + ': ' + msg) - if ftype == 'aiff': - return fname - if ftype is None or not ftype in table: - raise error('%s: unsupported audio file type %r' % (filename, ftype)) - (fd, temp) = tempfile.mkstemp() - os.close(fd) - temps.append(temp) - sts = table[ftype].copy(fname, temp) - if sts: - raise error(filename + ': conversion to aiff failed') - return temp Modified: python/branches/py3k-dtoa/Demo/tkinter/guido/AttrDialog.py ============================================================================== --- python/branches/py3k-dtoa/Demo/tkinter/guido/AttrDialog.py (original) +++ python/branches/py3k-dtoa/Demo/tkinter/guido/AttrDialog.py Sun Aug 8 12:41:24 2010 @@ -120,7 +120,7 @@ cl = self.classes[c] except KeyError: cl = 'unknown' - if type(cl) == TupleType: + if type(cl) == tuple: cl = self.enumoption elif cl == 'boolean': cl = self.booleanoption Modified: python/branches/py3k-dtoa/Demo/tkinter/guido/ManPage.py ============================================================================== --- python/branches/py3k-dtoa/Demo/tkinter/guido/ManPage.py (original) +++ python/branches/py3k-dtoa/Demo/tkinter/guido/ManPage.py Sun Aug 8 12:41:24 2010 @@ -107,13 +107,13 @@ # Save this line -- we need one line read-ahead self.buffer = nextline return - if emptyprog.match(self.buffer) >= 0: + if emptyprog.match(self.buffer): # Buffered line was empty -- set a flag self.empty = 1 self.buffer = nextline return textline = self.buffer - if ulprog.match(nextline) >= 0: + if ulprog.match(nextline): # Next line is properties for buffered line propline = nextline self.buffer = None @@ -127,7 +127,7 @@ self.ok = 1 self.empty = 0 return - if footerprog.match(textline) >= 0: + if footerprog.match(textline): # Footer -- start skipping until next non-blank line self.ok = 0 self.empty = 0 @@ -190,7 +190,7 @@ import os import sys # XXX This directory may be different on your system - MANDIR = '/usr/local/man/mann' + MANDIR = '' DEFAULTPAGE = 'Tcl' formatted = 0 if sys.argv[1:] and sys.argv[1] == '-f': Modified: python/branches/py3k-dtoa/Demo/tkinter/guido/brownian2.py ============================================================================== --- python/branches/py3k-dtoa/Demo/tkinter/guido/brownian2.py (original) +++ python/branches/py3k-dtoa/Demo/tkinter/guido/brownian2.py Sun Aug 8 12:41:24 2010 @@ -32,7 +32,7 @@ yield None def move(particle): # move the particle at random time - particle.next() + next(particle) dt = random.expovariate(LAMBDA) root.after(int(dt*1000), move, particle) Modified: python/branches/py3k-dtoa/Demo/tkinter/guido/kill.py ============================================================================== --- python/branches/py3k-dtoa/Demo/tkinter/guido/kill.py (original) +++ python/branches/py3k-dtoa/Demo/tkinter/guido/kill.py Sun Aug 8 12:41:24 2010 @@ -2,8 +2,6 @@ # Tkinter interface to Linux `kill' command. from tkinter import * -from string import splitfields -from string import split import subprocess import os @@ -26,13 +24,13 @@ ('Hex', '-X', 0)] def kill(self, selected): c = self.format_list[self.format.get()][2] - pid = split(selected)[c] + pid = selected.split()[c] os.system('kill -9 ' + pid) self.do_update() def do_update(self): name, option, column = self.format_list[self.format.get()] s = subprocess.getoutput('ps -w ' + option) - list = splitfields(s, '\n') + list = s.split('\n') self.header.set(list[0]) del list[0] y = self.frame.vscroll.get()[0] Modified: python/branches/py3k-dtoa/Demo/zlib/minigzip.py ============================================================================== --- python/branches/py3k-dtoa/Demo/zlib/minigzip.py (original) +++ python/branches/py3k-dtoa/Demo/zlib/minigzip.py Sun Aug 8 12:41:24 2010 @@ -10,10 +10,10 @@ FTEXT, FHCRC, FEXTRA, FNAME, FCOMMENT = 1, 2, 4, 8, 16 def write32(output, value): - output.write(chr(value & 255)) ; value=value // 256 - output.write(chr(value & 255)) ; value=value // 256 - output.write(chr(value & 255)) ; value=value // 256 - output.write(chr(value & 255)) + output.write(bytes([value & 255])) ; value=value // 256 + output.write(bytes([value & 255])) ; value=value // 256 + output.write(bytes([value & 255])) ; value=value // 256 + output.write(bytes([value & 255])) def read32(input): v = ord(input.read(1)) @@ -22,23 +22,24 @@ v += (ord(input.read(1)) << 24) return v -def compress (filename, input, output): - output.write('\037\213\010') # Write the header, ... - output.write(chr(FNAME)) # ... flag byte ... +def compress(filename, input, output): + output.write(b'\037\213\010') # Write the header, ... + output.write(bytes([FNAME])) # ... flag byte ... - statval = os.stat(filename) # ... modification time ... + statval = os.stat(filename) # ... modification time ... mtime = statval[8] write32(output, mtime) - output.write('\002') # ... slowest compression alg. ... - output.write('\377') # ... OS (=unknown) ... - output.write(filename+'\000') # ... original filename ... + output.write(b'\002') # ... slowest compression alg. ... + output.write(b'\377') # ... OS (=unknown) ... + bfilename = filename.encode(sys.getfilesystemencoding()) + output.write(bfilename + b'\000') # ... original filename ... - crcval = zlib.crc32("") + crcval = zlib.crc32(b'') compobj = zlib.compressobj(9, zlib.DEFLATED, -zlib.MAX_WBITS, zlib.DEF_MEM_LEVEL, 0) while True: data = input.read(1024) - if data == "": + if data == b'': break crcval = zlib.crc32(data, crcval) output.write(compobj.compress(data)) @@ -46,9 +47,9 @@ write32(output, crcval) # ... the CRC ... write32(output, statval[6]) # and the file size. -def decompress (input, output): +def decompress(input, output): magic = input.read(2) - if magic != '\037\213': + if magic != b'\037\213': print('Not a gzipped file') sys.exit(0) if ord(input.read(1)) != 8: @@ -66,21 +67,21 @@ # Read and discard a null-terminated string containing the filename while True: s = input.read(1) - if s == '\0': break + if s == b'\0': break if flag & FCOMMENT: # Read and discard a null-terminated string containing a comment while True: - s=input.read(1) - if s=='\0': break + s = input.read(1) + if s == b'\0': break if flag & FHCRC: input.read(2) # Read & discard the 16-bit header CRC decompobj = zlib.decompressobj(-zlib.MAX_WBITS) - crcval = zlib.crc32("") + crcval = zlib.crc32(b'') length = 0 while True: - data=input.read(1024) - if data == "": + data = input.read(1024) + if data == b"": break decompdata = decompobj.decompress(data) output.write(decompdata) Modified: python/branches/py3k-dtoa/Demo/zlib/zlibdemo.py ============================================================================== --- python/branches/py3k-dtoa/Demo/zlib/zlibdemo.py (original) +++ python/branches/py3k-dtoa/Demo/zlib/zlibdemo.py Sun Aug 8 12:41:24 2010 @@ -13,9 +13,8 @@ filename = sys.argv[0] print('Reading', filename) - f = open(filename, 'rb') # Get the data to compress - s = f.read() - f.close() + with open(filename, 'rb') as f: # Get the data to compress + s = f.read() # First, we'll compress the string in one step comptext = zlib.compress(s, 1) @@ -30,15 +29,15 @@ chunk = 256 compressor = zlib.compressobj(9) decompressor = zlib.decompressobj() - comptext = decomp = '' + comptext = decomp = b'' for i in range(0, len(s), chunk): - comptext = comptext+compressor.compress(s[i:i+chunk]) + comptext += compressor.compress(s[i:i+chunk]) # Don't forget to call flush()!! - comptext = comptext + compressor.flush() + comptext += compressor.flush() for i in range(0, len(comptext), chunk): - decomp = decomp + decompressor.decompress(comptext[i:i+chunk]) - decomp=decomp+decompressor.flush() + decomp += decompressor.decompress(comptext[i:i+chunk]) + decomp += decompressor.flush() print('Progressive compression (level 9):') print(' Original:', len(s), 'Compressed:', len(comptext), end=' ') Modified: python/branches/py3k-dtoa/Doc/Makefile ============================================================================== --- python/branches/py3k-dtoa/Doc/Makefile (original) +++ python/branches/py3k-dtoa/Doc/Makefile Sun Aug 8 12:41:24 2010 @@ -40,7 +40,7 @@ checkout: @if [ ! -d tools/sphinx ]; then \ echo "Checking out Sphinx..."; \ - svn checkout $(SVNROOT)/external/Sphinx-0.6.5/sphinx tools/sphinx; \ + svn checkout $(SVNROOT)/external/Sphinx-1.0.1/sphinx tools/sphinx; \ fi @if [ ! -d tools/docutils ]; then \ echo "Checking out Docutils..."; \ @@ -120,7 +120,7 @@ -rm -rf tools/docutils dist: - -rm -rf dist + rm -rf dist mkdir -p dist # archive the HTML @@ -142,15 +142,17 @@ rm dist/python-$(DISTVERSION)-docs-text.tar # archive the A4 latex - -rm -r build/latex + rm -rf build/latex make latex PAPER=a4 + -sed -i 's/makeindex/makeindex -q/' build/latex/Makefile (cd build/latex; make clean && make all-pdf && make FMT=pdf zip bz2) cp build/latex/docs-pdf.zip dist/python-$(DISTVERSION)-docs-pdf-a4.zip cp build/latex/docs-pdf.tar.bz2 dist/python-$(DISTVERSION)-docs-pdf-a4.tar.bz2 # archive the letter latex - rm -r build/latex + rm -rf build/latex make latex PAPER=letter + -sed -i 's/makeindex/makeindex -q/' build/latex/Makefile (cd build/latex; make clean && make all-pdf && make FMT=pdf zip bz2) cp build/latex/docs-pdf.zip dist/python-$(DISTVERSION)-docs-pdf-letter.zip cp build/latex/docs-pdf.tar.bz2 dist/python-$(DISTVERSION)-docs-pdf-letter.tar.bz2 Modified: python/branches/py3k-dtoa/Doc/README.txt ============================================================================== --- python/branches/py3k-dtoa/Doc/README.txt (original) +++ python/branches/py3k-dtoa/Doc/README.txt Sun Aug 8 12:41:24 2010 @@ -78,7 +78,7 @@ You'll need to install the Sphinx package, either by checking it out via :: - svn co http://svn.python.org/projects/external/Sphinx-0.6.5/sphinx tools/sphinx + svn co http://svn.python.org/projects/external/Sphinx-1.0.1/sphinx tools/sphinx or by installing it from PyPI. @@ -132,7 +132,7 @@ as long as you don't change or remove the copyright notice: ---------------------------------------------------------------------- -Copyright (c) 2000-2008 Python Software Foundation. +Copyright (c) 2000-2010 Python Software Foundation. All rights reserved. Copyright (c) 2000 BeOpen.com. Modified: python/branches/py3k-dtoa/Doc/c-api/init.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/c-api/init.rst (original) +++ python/branches/py3k-dtoa/Doc/c-api/init.rst Sun Aug 8 12:41:24 2010 @@ -959,7 +959,7 @@ .. cvar:: int PyTrace_C_EXCEPTION The value for the *what* parameter to :ctype:`Py_tracefunc` functions when a C - function has thrown an exception. + function has raised an exception. .. cvar:: int PyTrace_C_RETURN Modified: python/branches/py3k-dtoa/Doc/c-api/unicode.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/c-api/unicode.rst (original) +++ python/branches/py3k-dtoa/Doc/c-api/unicode.rst Sun Aug 8 12:41:24 2010 @@ -376,7 +376,7 @@ :cdata:`Py_FileSystemEncoding` should be used as the encoding, and ``"surrogateescape"`` should be used as the error handler (:pep:`383`). To encode file names during argument parsing, the ``"O&"`` converter should be -used, passsing :func:PyUnicode_FSConverter as the conversion function: +used, passsing :func:`PyUnicode_FSConverter` as the conversion function: .. cfunction:: int PyUnicode_FSConverter(PyObject* obj, void* result) @@ -670,6 +670,38 @@ Return *NULL* if an exception was raised by the codec. +UTF-7 Codecs +"""""""""""" + +These are the UTF-7 codec APIs: + + +.. cfunction:: PyObject* PyUnicode_DecodeUTF7(const char *s, Py_ssize_t size, const char *errors) + + Create a Unicode object by decoding *size* bytes of the UTF-7 encoded string + *s*. Return *NULL* if an exception was raised by the codec. + + +.. cfunction:: PyObject* PyUnicode_DecodeUTF8Stateful(const char *s, Py_ssize_t size, const char *errors, Py_ssize_t *consumed) + + If *consumed* is *NULL*, behave like :cfunc:`PyUnicode_DecodeUTF7`. If + *consumed* is not *NULL*, trailing incomplete UTF-7 base-64 sections will not + be treated as an error. Those bytes will not be decoded and the number of + bytes that have been decoded will be stored in *consumed*. + + +.. cfunction:: PyObject* PyUnicode_EncodeUTF7(const Py_UNICODE *s, Py_ssize_t size, int base64SetO, int base64WhiteSpace, const char *errors) + + Encode the :ctype:`Py_UNICODE` buffer of the given size using UTF-7 and + return a Python bytes object. Return *NULL* if an exception was raised by + the codec. + + If *base64SetO* is nonzero, "Set O" (punctuation that has no otherwise + special meaning) will be encoded in base-64. If *base64WhiteSpace* is + nonzero, whitespace will be encoded in base-64. Both are set to zero for the + Python "utf-7" codec. + + Unicode-Escape Codecs """"""""""""""""""""" Modified: python/branches/py3k-dtoa/Doc/c-api/weakref.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/c-api/weakref.rst (original) +++ python/branches/py3k-dtoa/Doc/c-api/weakref.rst Sun Aug 8 12:41:24 2010 @@ -53,7 +53,14 @@ .. cfunction:: PyObject* PyWeakref_GetObject(PyObject *ref) Return the referenced object from a weak reference, *ref*. If the referent is - no longer live, returns ``None``. + no longer live, returns :const:`Py_None`. + + .. warning:: + + This function returns a **borrowed reference** to the referenced object. + This means that you should always call :cfunc:`Py_INCREF` on the object + except if you know that it cannot be destroyed while you are still + using it. .. cfunction:: PyObject* PyWeakref_GET_OBJECT(PyObject *ref) Modified: python/branches/py3k-dtoa/Doc/conf.py ============================================================================== --- python/branches/py3k-dtoa/Doc/conf.py (original) +++ python/branches/py3k-dtoa/Doc/conf.py Sun Aug 8 12:41:24 2010 @@ -66,6 +66,9 @@ # Options for HTML output # ----------------------- +html_theme = 'default' +html_theme_options = {'collapsiblesidebar': True} + # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. html_last_updated_fmt = '%b %d, %Y' @@ -152,7 +155,7 @@ latex_appendices = ['glossary', 'about', 'license', 'copyright'] # Get LaTeX to handle Unicode correctly -latex_elements = {'inputenc': r'\usepackage[utf8x]{inputenc}'} +latex_elements = {'inputenc': r'\usepackage[utf8x]{inputenc}', 'utf8extra': ''} # Options for the coverage checker # -------------------------------- Modified: python/branches/py3k-dtoa/Doc/distutils/apiref.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/distutils/apiref.rst (original) +++ python/branches/py3k-dtoa/Doc/distutils/apiref.rst Sun Aug 8 12:41:24 2010 @@ -995,7 +995,7 @@ errors are ignored (apart from being reported to ``sys.stdout`` if *verbose* is true). -**\*\*** Some of this could be replaced with the shutil module? **\*\*** +.. XXX Some of this could be replaced with the shutil module? :mod:`distutils.file_util` --- Single file operations @@ -1311,9 +1311,7 @@ the "negative alias" of :option:`--verbose`, then :option:`--quiet` on the command line sets *verbose* to false. -**\*\*** Should be replaced with :mod:`optik` (which is also now known as -:mod:`optparse` in Python 2.3 and later). **\*\*** - +.. XXX Should be replaced with optparse .. function:: fancy_getopt(options, negative_opt, object, args) @@ -1944,6 +1942,19 @@ .. % todo +:mod:`distutils.command.check` --- Check the meta-data of a package +=================================================================== + +.. module:: distutils.command.check + :synopsis: Check the metadata of a package + + +The ``check`` command performs some tests on the meta-data of a package. +For example, it verifies that all required meta-data are provided as +the arguments passed to the :func:`setup` function. + +.. % todo + Creating a new Distutils command ================================ Modified: python/branches/py3k-dtoa/Doc/distutils/builtdist.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/distutils/builtdist.rst (original) +++ python/branches/py3k-dtoa/Doc/distutils/builtdist.rst Sun Aug 8 12:41:24 2010 @@ -141,13 +141,13 @@ commands. -.. _creating-dumb: +.. .. _creating-dumb: -Creating dumb built distributions -================================= +.. Creating dumb built distributions +.. ================================= -**\*\*** Need to document absolute vs. prefix-relative packages here, but first -I have to implement it! **\*\*** +.. XXX Need to document absolute vs. prefix-relative packages here, but first + I have to implement it! .. _creating-rpms: @@ -176,7 +176,7 @@ explicitly specify multiple :command:`bdist_\*` commands and their options:: python setup.py bdist_rpm --packager="John Doe " \ - bdist_wininst --target_version="2.0" + bdist_wininst --target-version="2.0" Creating RPM packages is driven by a :file:`.spec` file, much as using the Distutils is driven by the setup script. To make your life easier, the @@ -241,8 +241,7 @@ configuration file, :file:`setup.cfg`\ ---see section :ref:`setup-config`. If you distribute or package many Python module distributions, you might want to put options that apply to all of them in your personal Distutils configuration -file (:file:`~/.pydistutils.cfg`). If you want to temporarily disable -this file, you can pass the --no-user-cfg option to setup.py. +file (:file:`~/.pydistutils.cfg`). There are three steps to building a binary RPM package, all of which are handled automatically by the Distutils: Modified: python/branches/py3k-dtoa/Doc/distutils/commandref.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/distutils/commandref.rst (original) +++ python/branches/py3k-dtoa/Doc/distutils/commandref.rst Sun Aug 8 12:41:24 2010 @@ -48,6 +48,50 @@ .. % \label{clean-cmd} +.. _sdist-cmd: + +Creating a source distribution: the :command:`sdist` command +============================================================ + +.. XXX fragment moved down from above: needs context! + +The manifest template commands are: + ++-------------------------------------------+-----------------------------------------------+ +| Command | Description | ++===========================================+===============================================+ +| :command:`include pat1 pat2 ...` | include all files matching any of the listed | +| | patterns | ++-------------------------------------------+-----------------------------------------------+ +| :command:`exclude pat1 pat2 ...` | exclude all files matching any of the listed | +| | patterns | ++-------------------------------------------+-----------------------------------------------+ +| :command:`recursive-include dir pat1 pat2 | include all files under *dir* matching any of | +| ...` | the listed patterns | ++-------------------------------------------+-----------------------------------------------+ +| :command:`recursive-exclude dir pat1 pat2 | exclude all files under *dir* matching any of | +| ...` | the listed patterns | ++-------------------------------------------+-----------------------------------------------+ +| :command:`global-include pat1 pat2 ...` | include all files anywhere in the source tree | +| | matching --- & any of the listed patterns | ++-------------------------------------------+-----------------------------------------------+ +| :command:`global-exclude pat1 pat2 ...` | exclude all files anywhere in the source tree | +| | matching --- & any of the listed patterns | ++-------------------------------------------+-----------------------------------------------+ +| :command:`prune dir` | exclude all files under *dir* | ++-------------------------------------------+-----------------------------------------------+ +| :command:`graft dir` | include all files under *dir* | ++-------------------------------------------+-----------------------------------------------+ + +The patterns here are Unix-style "glob" patterns: ``*`` matches any sequence of +regular filename characters, ``?`` matches any single regular filename +character, and ``[range]`` matches any of the characters in *range* (e.g., +``a-z``, ``a-zA-Z``, ``a-f0-9_.``). The definition of "regular filename +character" is platform-specific: on Unix it is anything except slash; on Windows +anything except backslash or colon. + +.. XXX Windows support not there yet + .. % \section{Creating a built distribution: the .. % \protect\command{bdist} command family} .. % \label{bdist-cmds} Modified: python/branches/py3k-dtoa/Doc/distutils/examples.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/distutils/examples.rst (original) +++ python/branches/py3k-dtoa/Doc/distutils/examples.rst Sun Aug 8 12:41:24 2010 @@ -233,6 +233,58 @@ ext_modules=[Extension('foopkg.foo', ['foo.c'])], ) +Checking a package +================== + +The ``check`` command allows you to verify if your package meta-data +meet the minimum requirements to build a distribution. + +To run it, just call it using your :file:`setup.py` script. If something is +missing, ``check`` will display a warning. + +Let's take an example with a simple script:: + + from distutils.core import setup + + setup(name='foobar') + +Running the ``check`` command will display some warnings:: + + $ python setup.py check + running check + warning: check: missing required meta-data: version, url + warning: check: missing meta-data: either (author and author_email) or + (maintainer and maintainer_email) must be supplied + + +If you use the reStructuredText syntax in the `long_description` field and +`docutils `_ is installed you can check if +the syntax is fine with the ``check`` command, using the `restructuredtext` +option. + +For example, if the :file:`setup.py` script is changed like this:: + + from distutils.core import setup + + desc = """\ + My description + ============= + + This is the description of the ``foobar`` package. + """ + + setup(name='foobar', version='1', author='tarek', + author_email='tarek at ziade.org', + url='http://example.com', long_description=desc) + +Where the long description is broken, ``check`` will be able to detect it +by using the `docutils` parser:: + + $ pythontrunk setup.py check --restructuredtext + running check + warning: check: Title underline too short. (line 2) + warning: check: Could not finish the parsing. + .. % \section{Multiple extension modules} .. % \label{multiple-ext} Modified: python/branches/py3k-dtoa/Doc/distutils/setupscript.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/distutils/setupscript.rst (original) +++ python/branches/py3k-dtoa/Doc/distutils/setupscript.rst Sun Aug 8 12:41:24 2010 @@ -207,7 +207,7 @@ SWIG on the interface file and compile the resulting C/C++ file into your extension. -**\*\*** SWIG support is rough around the edges and largely untested! **\*\*** +.. XXX SWIG support is rough around the edges and largely untested! This warning notwithstanding, options to SWIG can be currently passed like this:: @@ -326,7 +326,7 @@ (Again, this sort of non-portable construct should be avoided if you intend to distribute your code.) -**\*\*** Should mention clib libraries here or somewhere else! **\*\*** +.. XXX Should mention clib libraries here or somewhere else! Other options Modified: python/branches/py3k-dtoa/Doc/distutils/sourcedist.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/distutils/sourcedist.rst (original) +++ python/branches/py3k-dtoa/Doc/distutils/sourcedist.rst Sun Aug 8 12:41:24 2010 @@ -26,16 +26,16 @@ +===========+=========================+=========+ | ``zip`` | zip file (:file:`.zip`) | (1),(3) | +-----------+-------------------------+---------+ -| ``gztar`` | gzip'ed tar file | \(2) | +| ``gztar`` | gzip'ed tar file | (2),(4) | | | (:file:`.tar.gz`) | | +-----------+-------------------------+---------+ -| ``bztar`` | bzip2'ed tar file | | +| ``bztar`` | bzip2'ed tar file | \(4) | | | (:file:`.tar.bz2`) | | +-----------+-------------------------+---------+ | ``ztar`` | compressed tar file | \(4) | | | (:file:`.tar.Z`) | | +-----------+-------------------------+---------+ -| ``tar`` | tar file (:file:`.tar`) | | +| ``tar`` | tar file (:file:`.tar`) | \(4) | +-----------+-------------------------+---------+ Notes: @@ -51,16 +51,8 @@ of the standard Python library since Python 1.6) (4) - requires the :program:`compress` program. Notice that this format is now - pending for deprecation and will be removed in the future versions of Python. - -When using any ``tar`` format (``gztar``, ``bztar``, ``ztar`` or -``tar``) under Unix, you can specify the ``owner`` and ``group`` names -that will be set for each member of the archive. - -For example, if you want all files of the archive to be owned by root:: - - python setup.py sdist --owner=root --group=root + requires external utilities: :program:`tar` and possibly one of :program:`gzip`, + :program:`bzip2`, or :program:`compress` .. _manifest: @@ -78,8 +70,8 @@ * all C source files mentioned in the :option:`ext_modules` or :option:`libraries` options ( - **\*\*** getting C library sources currently broken---no - :meth:`get_source_files` method in :file:`build_clib.py`! **\*\***) + .. XXX getting C library sources currently broken---no + :meth:`get_source_files` method in :file:`build_clib.py`! * scripts identified by the :option:`scripts` option See :ref:`distutils-installing-scripts`. @@ -111,60 +103,9 @@ :file:`MANIFEST`, you must specify everything: the default set of files described above does not apply in this case. -See :ref:`manifest_template` section for a syntax reference. - -.. _manifest-options: - -Manifest-related options -======================== - -The normal course of operations for the :command:`sdist` command is as follows: - -* if the manifest file, :file:`MANIFEST` doesn't exist, read :file:`MANIFEST.in` - and create the manifest - -* if neither :file:`MANIFEST` nor :file:`MANIFEST.in` exist, create a manifest - with just the default file set - -* if either :file:`MANIFEST.in` or the setup script (:file:`setup.py`) are more - recent than :file:`MANIFEST`, recreate :file:`MANIFEST` by reading - :file:`MANIFEST.in` - -* use the list of files now in :file:`MANIFEST` (either just generated or read - in) to create the source distribution archive(s) - -There are a couple of options that modify this behaviour. First, use the -:option:`--no-defaults` and :option:`--no-prune` to disable the standard -"include" and "exclude" sets. - -Second, you might just want to (re)generate the manifest, but not create a -source distribution:: - - python setup.py sdist --manifest-only - -:option:`-o` is a sortcut for :option:`--manifest-only`. - -.. _manifest_template: - -The MANIFEST.in template -======================== - -A :file:`MANIFEST.in` file can be added in a project to define the list of -files to include in the distribution built by the :command:`sdist` command. - -When :command:`sdist` is run, it will look for the :file:`MANIFEST.in` file -and interpret it to generate the :file:`MANIFEST` file that contains the -list of files that will be included in the package. - -This mechanism can be used when the default list of files is not enough. -(See :ref:`manifest`). - -Principle ---------- - The manifest template has one command per line, where each command specifies a set of files to include or exclude from the source distribution. For an -example, let's look at the Distutils' own manifest template:: +example, again we turn to the Distutils' own manifest template:: include *.txt recursive-include examples *.txt *.py @@ -176,7 +117,9 @@ :file:`examples/sample?/build`. All of this is done *after* the standard include set, so you can exclude files from the standard set with explicit instructions in the manifest template. (Or, you can use the -:option:`--no-defaults` option to disable the standard set entirely.) +:option:`--no-defaults` option to disable the standard set entirely.) There are +several other commands available in the manifest template mini-language; see +section :ref:`sdist-cmd`. The order of commands in the manifest template matters: initially, we have the list of default files as described above, and each command in the template adds @@ -230,41 +173,36 @@ them to the standard representation on your platform. That way, the manifest template is portable across operating systems. -Commands --------- -The manifest template commands are: +.. _manifest-options: + +Manifest-related options +======================== + +The normal course of operations for the :command:`sdist` command is as follows: + +* if the manifest file, :file:`MANIFEST` doesn't exist, read :file:`MANIFEST.in` + and create the manifest + +* if neither :file:`MANIFEST` nor :file:`MANIFEST.in` exist, create a manifest + with just the default file set + +* if either :file:`MANIFEST.in` or the setup script (:file:`setup.py`) are more + recent than :file:`MANIFEST`, recreate :file:`MANIFEST` by reading + :file:`MANIFEST.in` + +* use the list of files now in :file:`MANIFEST` (either just generated or read + in) to create the source distribution archive(s) + +There are a couple of options that modify this behaviour. First, use the +:option:`--no-defaults` and :option:`--no-prune` to disable the standard +"include" and "exclude" sets. + +Second, you might just want to (re)generate the manifest, but not create a source +distribution:: + + python setup.py sdist --manifest-only + +:option:`-o` is a shortcut for :option:`--manifest-only`. -+-------------------------------------------+-----------------------------------------------+ -| Command | Description | -+===========================================+===============================================+ -| :command:`include pat1 pat2 ...` | include all files matching any of the listed | -| | patterns | -+-------------------------------------------+-----------------------------------------------+ -| :command:`exclude pat1 pat2 ...` | exclude all files matching any of the listed | -| | patterns | -+-------------------------------------------+-----------------------------------------------+ -| :command:`recursive-include dir pat1 pat2 | include all files under *dir* matching any of | -| ...` | the listed patterns | -+-------------------------------------------+-----------------------------------------------+ -| :command:`recursive-exclude dir pat1 pat2 | exclude all files under *dir* matching any of | -| ...` | the listed patterns | -+-------------------------------------------+-----------------------------------------------+ -| :command:`global-include pat1 pat2 ...` | include all files anywhere in the source tree | -| | matching --- & any of the listed patterns | -+-------------------------------------------+-----------------------------------------------+ -| :command:`global-exclude pat1 pat2 ...` | exclude all files anywhere in the source tree | -| | matching --- & any of the listed patterns | -+-------------------------------------------+-----------------------------------------------+ -| :command:`prune dir` | exclude all files under *dir* | -+-------------------------------------------+-----------------------------------------------+ -| :command:`graft dir` | include all files under *dir* | -+-------------------------------------------+-----------------------------------------------+ - -The patterns here are Unix-style "glob" patterns: ``*`` matches any sequence of -regular filename characters, ``?`` matches any single regular filename -character, and ``[range]`` matches any of the characters in *range* (e.g., -``a-z``, ``a-zA-Z``, ``a-f0-9_.``). The definition of "regular filename -character" is platform-specific: on Unix it is anything except slash; on Windows -anything except backslash or colon. Modified: python/branches/py3k-dtoa/Doc/distutils/uploading.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/distutils/uploading.rst (original) +++ python/branches/py3k-dtoa/Doc/distutils/uploading.rst Sun Aug 8 12:41:24 2010 @@ -60,13 +60,13 @@ setup(name='Distutils', long_description=open('README.txt')) -In that case, :file:`README.txt` is a regular reStructuredText text file located -in the root of the package besides :file:`setup.py`. +In that case, `README.txt` is a regular reStructuredText text file located +in the root of the package besides `setup.py`. To prevent registering broken reStructuredText content, you can use the -:program:`rst2html` program that is provided by the :mod:`docutils` package +:program:`rst2html` program that is provided by the `docutils` package and check the ``long_description`` from the command line:: $ python setup.py --long-description | rst2html.py > output.html -:mod:`docutils` will display a warning if there's something wrong with your syntax. +`docutils` will display a warning if there's something wrong with your syntax. Modified: python/branches/py3k-dtoa/Doc/documenting/markup.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/documenting/markup.rst (original) +++ python/branches/py3k-dtoa/Doc/documenting/markup.rst Sun Aug 8 12:41:24 2010 @@ -177,6 +177,37 @@ are modified), side effects, and possible exceptions. A small example may be provided. +.. describe:: decorator + + Describes a decorator function. The signature should *not* represent the + signature of the actual function, but the usage as a decorator. For example, + given the functions + + .. code-block:: python + + def removename(func): + func.__name__ = '' + return func + + def setnewname(name): + def decorator(func): + func.__name__ = name + return func + return decorator + + the descriptions should look like this:: + + .. decorator:: removename + + Remove name of the decorated function. + + .. decorator:: setnewname(name) + + Set name of the decorated function to *name*. + + There is no ``deco`` role to link to a decorator that is marked up with + this directive; rather, use the ``:func:`` role. + .. describe:: class Describes a class. The signature can include parentheses with parameters @@ -194,14 +225,20 @@ parameter. The description should include similar information to that described for ``function``. +.. describe:: decoratormethod + + Same as ``decorator``, but for decorators that are methods. + + Refer to a decorator method using the ``:meth:`` role. + .. describe:: opcode Describes a Python :term:`bytecode` instruction. .. describe:: cmdoption - Describes a command line option or switch. Option argument names should be - enclosed in angle brackets. Example:: + Describes a Python command line option or switch. Option argument names + should be enclosed in angle brackets. Example:: .. cmdoption:: -m @@ -502,8 +539,9 @@ .. describe:: option - A command-line option to an executable program. The leading hyphen(s) must - be included. + A command-line option of Python. The leading hyphen(s) must be included. + If a matching ``cmdoption`` directive exists, it is linked to. For options + of other programs or scripts, use simple ````code```` markup. .. describe:: program Modified: python/branches/py3k-dtoa/Doc/extending/extending.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/extending/extending.rst (original) +++ python/branches/py3k-dtoa/Doc/extending/extending.rst Sun Aug 8 12:41:24 2010 @@ -226,9 +226,28 @@ become a dangling pointer. Should it become a dangling pointer, C code which raises the exception could cause a core dump or other unintended side effects. -We discuss the use of PyMODINIT_FUNC as a function return type later in this +We discuss the use of ``PyMODINIT_FUNC`` as a function return type later in this sample. +The :exc:`spam.error` exception can be raised in your extension module using a +call to :cfunc:`PyErr_SetString` as shown below:: + + static PyObject * + spam_system(PyObject *self, PyObject *args) + { + const char *command; + int sts; + + if (!PyArg_ParseTuple(args, "s", &command)) + return NULL; + sts = system(command); + if (sts < 0) { + PyErr_SetString(SpamError, "System command failed"); + return NULL; + } + return PyLong_FromLong(sts); + } + .. _backtoexample: Modified: python/branches/py3k-dtoa/Doc/faq/extending.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/faq/extending.rst (original) +++ python/branches/py3k-dtoa/Doc/faq/extending.rst Sun Aug 8 12:41:24 2010 @@ -29,6 +29,8 @@ C++ objects with constructors are probably not a good idea. +.. _c-wrapper-software: + Writing C is hard; are there any alternatives? ---------------------------------------------- @@ -200,11 +202,7 @@ whole lot of difference between C and C++ -- so the strategy of building a new Python type around a C structure (pointer) type will also work for C++ objects. -For C++ libraries, you can look at `SIP -`_, `CXX -`_, `Boost -`_, `Weave -`_ or `SWIG `_ +For C++ libraries, see :ref:`c-wrapper-software`. I added a module using the Setup file and the make fails; why? Modified: python/branches/py3k-dtoa/Doc/glossary.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/glossary.rst (original) +++ python/branches/py3k-dtoa/Doc/glossary.rst Sun Aug 8 12:41:24 2010 @@ -154,15 +154,15 @@ object. duck-typing - A pythonic programming style which determines an object's type by inspection - of its method or attribute signature rather than by explicit relationship - to some type object ("If it looks like a duck and quacks like a duck, it + A programming style which does not look at an object's type to determine + if it has the right interface; instead, the method or attribute is simply + called or used ("If it looks like a duck and quacks like a duck, it must be a duck.") By emphasizing interfaces rather than specific types, well-designed code improves its flexibility by allowing polymorphic substitution. Duck-typing avoids tests using :func:`type` or - :func:`isinstance`. (Note, however, that duck-typing can be complemented - with abstract base classes.) Instead, it typically employs :func:`hasattr` - tests or :term:`EAFP` programming. + :func:`isinstance`. (Note, however, that duck-typing can be complemented + with :term:`abstract base class`\ es.) Instead, it typically employs + :func:`hasattr` tests or :term:`EAFP` programming. EAFP Easier to ask for forgiveness than permission. This common Python coding Modified: python/branches/py3k-dtoa/Doc/howto/doanddont.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/howto/doanddont.rst (original) +++ python/branches/py3k-dtoa/Doc/howto/doanddont.rst Sun Aug 8 12:41:24 2010 @@ -154,7 +154,7 @@ Consider the case the file gets deleted between the time the call to :func:`os.path.exists` is made and the time :func:`open` is called. That means -the last line will throw an :exc:`IOError`. The same would happen if *file* +the last line will raise an :exc:`IOError`. The same would happen if *file* exists but has no read permission. Since testing this on a normal machine on existing and non-existing files make it seem bugless, that means in testing the results will seem fine, and the code will get shipped. Then an unhandled Modified: python/branches/py3k-dtoa/Doc/install/index.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/install/index.rst (original) +++ python/branches/py3k-dtoa/Doc/install/index.rst Sun Aug 8 12:41:24 2010 @@ -314,8 +314,8 @@ stash of Python modules. This scheme's name is derived from the idea of a "home" directory on Unix, since it's not unusual for a Unix user to make their home directory have a layout similar to :file:`/usr/` or :file:`/usr/local/`. -This scheme can be used by anyone, regardless of the operating system their -installing for. +This scheme can be used by anyone, regardless of the operating system they +are installing for. Installing a new module distribution is as simple as :: Modified: python/branches/py3k-dtoa/Doc/library/2to3.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/library/2to3.rst (original) +++ python/branches/py3k-dtoa/Doc/library/2to3.rst Sun Aug 8 12:41:24 2010 @@ -267,6 +267,25 @@ Converts octal literals into the new syntax. +.. 2to3fixer:: operator + + Converts calls to various functions in the :mod:`operator` module to other, + but equivalent, function calls. When needed, the appropriate ``import`` + statements are added, e.g. ``import collections``. The following mapping + are made: + + ================================== ========================================== + From To + ================================== ========================================== + ``operator.isCallable(obj)`` ``hasattr(obj, '__call__')`` + ``operator.sequenceIncludes(obj)`` ``operator.contains(obj)`` + ``operator.isSequenceType(obj)`` ``isinstance(obj, collections.Sequence)`` + ``operator.isMappingType(obj)`` ``isinstance(obj, collections.Mapping)`` + ``operator.isNumberType(obj)`` ``isinstance(obj, numbers.Number)`` + ``operator.repeat(obj, n)`` ``operator.mul(obj, n)`` + ``operator.irepeat(obj, n)`` ``operator.imul(obj, n)`` + ================================== ========================================== + .. 2to3fixer:: paren Add extra parenthesis where they are required in list comprehensions. For Modified: python/branches/py3k-dtoa/Doc/library/abc.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/library/abc.rst (original) +++ python/branches/py3k-dtoa/Doc/library/abc.rst Sun Aug 8 12:41:24 2010 @@ -122,7 +122,7 @@ It also provides the following decorators: -.. function:: abstractmethod(function) +.. decorator:: abstractmethod(function) A decorator indicating abstract methods. Modified: python/branches/py3k-dtoa/Doc/library/argparse.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/library/argparse.rst (original) +++ python/branches/py3k-dtoa/Doc/library/argparse.rst Sun Aug 8 12:41:24 2010 @@ -203,8 +203,8 @@ add_help ^^^^^^^^ -By default, ArgumentParser objects add a ``-h/--help`` option which simply -displays the parser's help message. For example, consider a file named +By default, ArgumentParser objects add an option which simply displays +the parser's help message. For example, consider a file named ``myprogram.py`` containing the following code:: import argparse @@ -234,12 +234,27 @@ optional arguments: --foo FOO foo help +The help option is typically ``-h/--help``. The exception to this is +if the ``prefix_chars=`` is specified and does not include ``'-'``, in +which case ``-h`` and ``--help`` are not valid options. In +this case, the first character in ``prefix_chars`` is used to prefix +the help options:: + + >>> parser = argparse.ArgumentParser(prog='PROG', prefix_chars='+/') + >>> parser.print_help() + usage: PROG [+h] + + optional arguments: + +h, ++help show this help message and exit + + prefix_chars ^^^^^^^^^^^^ Most command-line options will use ``'-'`` as the prefix, e.g. ``-f/--foo``. -Parsers that need to support additional prefix characters, e.g. for options +Parsers that need to support different or additional prefix +characters, e.g. for options like ``+f`` or ``/foo``, may specify them using the ``prefix_chars=`` argument to the ArgumentParser constructor:: @@ -698,8 +713,8 @@ >>> class FooAction(argparse.Action): ... def __call__(self, parser, namespace, values, option_string=None): - ... print('%r %r %r' % (namespace, values, option_string)) - ... setattr(namespace, self.dest, values) + ... print('%r %r %r' % (namespace, values, option_string)) + ... setattr(namespace, self.dest, values) ... >>> parser = argparse.ArgumentParser() >>> parser.add_argument('--foo', action=FooAction) Modified: python/branches/py3k-dtoa/Doc/library/ast.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/library/ast.rst (original) +++ python/branches/py3k-dtoa/Doc/library/ast.rst Sun Aug 8 12:41:24 2010 @@ -119,12 +119,15 @@ Safely evaluate an expression node or a string containing a Python expression. The string or node provided may only consist of the following - Python literal structures: strings, numbers, tuples, lists, dicts, booleans, - and ``None``. + Python literal structures: strings, bytes, numbers, tuples, lists, dicts, + sets, booleans, and ``None``. This can be used for safely evaluating strings containing Python expressions from untrusted sources without the need to parse the values oneself. + .. versionchanged:: 3.2 + Now allows bytes and set literals. + .. function:: get_docstring(node, clean=True) Modified: python/branches/py3k-dtoa/Doc/library/base64.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/library/base64.rst (original) +++ python/branches/py3k-dtoa/Doc/library/base64.rst Sun Aug 8 12:41:24 2010 @@ -24,7 +24,7 @@ .. function:: b64encode(s, altchars=None) - Encode a string use Base64. + Encode a byte string use Base64. *s* is the string to encode. Optional *altchars* must be a string of at least length 2 (additional characters are ignored) which specifies an alternative @@ -32,54 +32,54 @@ generate URL or filesystem safe Base64 strings. The default is ``None``, for which the standard Base64 alphabet is used. - The encoded string is returned. + The encoded byte string is returned. .. function:: b64decode(s, altchars=None) - Decode a Base64 encoded string. + Decode a Base64 encoded byte string. *s* is the string to decode. Optional *altchars* must be a string of at least length 2 (additional characters are ignored) which specifies the alternative alphabet used instead of the ``+`` and ``/`` characters. - The decoded string is returned. A :exc:`TypeError` is raised if *s* were + The decoded byte string is returned. A :exc:`TypeError` is raised if *s* were incorrectly padded or if there are non-alphabet characters present in the string. .. function:: standard_b64encode(s) - Encode string *s* using the standard Base64 alphabet. + Encode byte string *s* using the standard Base64 alphabet. .. function:: standard_b64decode(s) - Decode string *s* using the standard Base64 alphabet. + Decode byte string *s* using the standard Base64 alphabet. .. function:: urlsafe_b64encode(s) - Encode string *s* using a URL-safe alphabet, which substitutes ``-`` instead of + Encode byte string *s* using a URL-safe alphabet, which substitutes ``-`` instead of ``+`` and ``_`` instead of ``/`` in the standard Base64 alphabet. The result can still contain ``=``. .. function:: urlsafe_b64decode(s) - Decode string *s* using a URL-safe alphabet, which substitutes ``-`` instead of + Decode byte string *s* using a URL-safe alphabet, which substitutes ``-`` instead of ``+`` and ``_`` instead of ``/`` in the standard Base64 alphabet. .. function:: b32encode(s) - Encode a string using Base32. *s* is the string to encode. The encoded string + Encode a byte string using Base32. *s* is the string to encode. The encoded string is returned. .. function:: b32decode(s, casefold=False, map01=None) - Decode a Base32 encoded string. + Decode a Base32 encoded byte string. *s* is the string to decode. Optional *casefold* is a flag specifying whether a lowercase alphabet is acceptable as input. For security purposes, the default @@ -92,27 +92,27 @@ digit 0 is always mapped to the letter O). For security purposes the default is ``None``, so that 0 and 1 are not allowed in the input. - The decoded string is returned. A :exc:`TypeError` is raised if *s* were + The decoded byte string is returned. A :exc:`TypeError` is raised if *s* were incorrectly padded or if there are non-alphabet characters present in the string. .. function:: b16encode(s) - Encode a string using Base16. + Encode a byte string using Base16. - *s* is the string to encode. The encoded string is returned. + *s* is the string to encode. The encoded byte string is returned. .. function:: b16decode(s, casefold=False) - Decode a Base16 encoded string. + Decode a Base16 encoded byte string. *s* is the string to decode. Optional *casefold* is a flag specifying whether a lowercase alphabet is acceptable as input. For security purposes, the default is ``False``. - The decoded string is returned. A :exc:`TypeError` is raised if *s* were + The decoded byte string is returned. A :exc:`TypeError` is raised if *s* were incorrectly padded or if there are non-alphabet characters present in the string. Modified: python/branches/py3k-dtoa/Doc/library/bdb.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/library/bdb.rst (original) +++ python/branches/py3k-dtoa/Doc/library/bdb.rst Sun Aug 8 12:41:24 2010 @@ -50,9 +50,10 @@ Mark the breakpoint as disabled. - .. method:: bpprint(out=None) + .. method:: bpformat() - Print all the information about the breakpoint: + Return a string with all the information about the breakpoint, nicely + formatted: * The breakpoint number. * If it is temporary or not. @@ -61,6 +62,13 @@ * If it must be ignored the next N times. * The breakpoint hit count. + .. versionadded:: 3.2 + + .. method:: bpprint(out=None) + + Print the output of :meth:`bpformat` to the file *out*, or if it is + ``None``, to standard output. + .. class:: Bdb(skip=None) @@ -108,7 +116,7 @@ * ``"exception"``: An exception has occurred. * ``"c_call"``: A C function is about to be called. * ``"c_return"``: A C function has returned. - * ``"c_exception"``: A C function has thrown an exception. + * ``"c_exception"``: A C function has raised an exception. For the Python events, specialized functions (see below) are called. For the C events, no action is taken. @@ -267,6 +275,15 @@ Delete all existing breakpoints. + .. method:: get_bpbynumber(arg) + + Return a breakpoint specified by the given number. If *arg* is a string, + it will be converted to a number. If *arg* is a non-numeric string, if + the given breakpoint never existed or has been deleted, a + :exc:`ValueError` is raised. + + .. versionadded:: 3.2 + .. method:: get_break(filename, lineno) Check if there is a breakpoint for *lineno* of *filename*. Modified: python/branches/py3k-dtoa/Doc/library/binascii.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/library/binascii.rst (original) +++ python/branches/py3k-dtoa/Doc/library/binascii.rst Sun Aug 8 12:41:24 2010 @@ -18,6 +18,11 @@ low-level functions written in C for greater speed that are used by the higher-level modules. +.. note:: + + Encoding and decoding functions do not accept Unicode strings. Only bytestring + and bytearray objects can be processed. + The :mod:`binascii` module defines the following functions: @@ -54,6 +59,9 @@ data. More than one line may be passed at a time. If the optional argument *header* is present and true, underscores will be decoded as spaces. + .. versionchanged:: 3.2 + accept only bytestring or bytearray object as input. + .. function:: b2a_qp(data, quotetabs=False, istext=True, header=False) @@ -83,6 +91,9 @@ decompressed data, unless data input data ends in an orphaned repeat indicator, in which case the :exc:`Incomplete` exception is raised. + .. versionchanged:: 3.2 + accept only bytestring or bytearray object as input. + .. function:: rlecode_hqx(data) @@ -139,6 +150,9 @@ of hexadecimal digits (which can be upper or lower case), otherwise a :exc:`TypeError` is raised. + .. versionchanged:: 3.2 + accept only bytestring or bytearray object as input. + .. exception:: Error @@ -164,4 +178,3 @@ Module :mod:`quopri` Support for quoted-printable encoding used in MIME email messages. - Modified: python/branches/py3k-dtoa/Doc/library/bisect.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/library/bisect.rst (original) +++ python/branches/py3k-dtoa/Doc/library/bisect.rst Sun Aug 8 12:41:24 2010 @@ -39,6 +39,9 @@ ``a.insert(bisect.bisect_left(a, x, lo, hi), x)``. This assumes that *a* is already sorted. + Also note that while the fast search step is O(log n), the slower insertion + step is O(n), so the overall operation is slow. + .. function:: insort_right(a, x, lo=0, hi=len(a)) insort(a, x, lo=0, hi=len(a)) @@ -46,9 +49,53 @@ Similar to :func:`insort_left`, but inserting *x* in *a* after any existing entries of *x*. + Also note that while the fast search step is O(log n), the slower insertion + step is O(n), so the overall operation is slow. + +Searching Sorted Lists +---------------------- + +The above :func:`bisect` functions are useful for finding insertion points, but +can be tricky or awkward to use for common searching tasks. The following three +functions show how to transform them into the standard lookups for sorted +lists:: + + def find(a, key): + '''Find leftmost item exact equal to the key. + Raise ValueError if no such item exists. + + ''' + i = bisect_left(a, key) + if i < len(a) and a[i] == key: + return a[i] + raise ValueError('No item found with key equal to: %r' % (key,)) + + def find_le(a, key): + '''Find largest item less-than or equal to key. + Raise ValueError if no such item exists. + If multiple keys are equal, return the leftmost. + + ''' + i = bisect_left(a, key) + if i < len(a) and a[i] == key: + return a[i] + if i == 0: + raise ValueError('No item found with key at or below: %r' % (key,)) + return a[i-1] + + def find_ge(a, key): + '''Find smallest item greater-than or equal to key. + Raise ValueError if no such item exists. + If multiple keys are equal, return the leftmost. + + ''' + i = bisect_left(a, key) + if i == len(a): + raise ValueError('No item found with key at or above: %r' % (key,)) + return a[i] -Examples --------- +Other Examples +-------------- .. _bisect-example: @@ -87,3 +134,10 @@ ('red', 5) >>> data[bisect_left(keys, 8)] ('yellow', 8) + +.. seealso:: + + `SortedCollection recipe + `_ that + encapsulates precomputed keys, allowing straight-forward insertion and + searching using a *key* function. Modified: python/branches/py3k-dtoa/Doc/library/cgi.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/library/cgi.rst (original) +++ python/branches/py3k-dtoa/Doc/library/cgi.rst Sun Aug 8 12:41:24 2010 @@ -324,10 +324,13 @@ Convert the characters ``'&'``, ``'<'`` and ``'>'`` in string *s* to HTML-safe sequences. Use this if you need to display text that might contain such characters in HTML. If the optional flag *quote* is true, the quotation mark - character (``'"'``) is also translated; this helps for inclusion in an HTML - attribute value, as in ````. If the value to be quoted might - include single- or double-quote characters, or both, consider using the - :func:`quoteattr` function in the :mod:`xml.sax.saxutils` module instead. + character (``"``) is also translated; this helps for inclusion in an HTML + attribute value delimited by double quotes, as in ````. Note + that single quotes are never translated. + + If the value to be quoted might include single- or double-quote characters, + or both, consider using the :func:`quoteattr` function in the + :mod:`xml.sax.saxutils` module instead. .. _cgi-security: Modified: python/branches/py3k-dtoa/Doc/library/cmath.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/library/cmath.rst (original) +++ python/branches/py3k-dtoa/Doc/library/cmath.rst Sun Aug 8 12:41:24 2010 @@ -187,15 +187,24 @@ Classification functions ------------------------ +.. function:: isfinite(x) + + Return ``True`` if both the real and imaginary parts of *x* are finite, and + ``False`` otherwise. + + .. versionadded:: 3.2 + + .. function:: isinf(x) - Return *True* if the real or the imaginary part of x is positive - or negative infinity. + Return ``True`` if either the real or the imaginary part of *x* is an + infinity, and ``False`` otherwise. .. function:: isnan(x) - Return *True* if the real or imaginary part of x is not a number (NaN). + Return ``True`` if either the real or the imaginary part of *x* is a NaN, + and ``False`` otherwise. Constants Modified: python/branches/py3k-dtoa/Doc/library/cmd.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/library/cmd.rst (original) +++ python/branches/py3k-dtoa/Doc/library/cmd.rst Sun Aug 8 12:41:24 2010 @@ -76,11 +76,13 @@ are the beginning and ending indexes of the prefix text, which could be used to provide different completion depending upon which position the argument is in. - All subclasses of :class:`Cmd` inherit a predefined :meth:`do_help`. This + All subclasses of :class:`Cmd` inherit a predefined :meth:`do_help`. This method, called with an argument ``'bar'``, invokes the corresponding method - :meth:`help_bar`. With no argument, :meth:`do_help` lists all available help - topics (that is, all commands with corresponding :meth:`help_\*` methods), and - also lists any undocumented commands. + :meth:`help_bar`, and if that is not present, prints the docstring of + :meth:`do_bar`, if available. With no argument, :meth:`do_help` lists all + available help topics (that is, all commands with corresponding + :meth:`help_\*` methods or commands that have docstrings), and also lists any + undocumented commands. .. method:: Cmd.onecmd(str) Modified: python/branches/py3k-dtoa/Doc/library/collections.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/library/collections.rst (original) +++ python/branches/py3k-dtoa/Doc/library/collections.rst Sun Aug 8 12:41:24 2010 @@ -12,131 +12,24 @@ import itertools __name__ = '' -This module implements high-performance container datatypes. Currently, -there are four datatypes, :class:`Counter`, :class:`deque`, :class:`OrderedDict` and -:class:`defaultdict`, and one datatype factory function, :func:`namedtuple`. - -The specialized containers provided in this module provide alternatives -to Python's general purpose built-in containers, :class:`dict`, -:class:`list`, :class:`set`, and :class:`tuple`. - -In addition to containers, the collections module provides some ABCs -(abstract base classes) that can be used to test whether a class -provides a particular interface, for example, whether it is hashable or -a mapping. - -ABCs - abstract base classes ----------------------------- - -The collections module offers the following ABCs: - -========================= ===================== ====================== ==================================================== -ABC Inherits Abstract Methods Mixin Methods -========================= ===================== ====================== ==================================================== -:class:`Container` ``__contains__`` -:class:`Hashable` ``__hash__`` -:class:`Iterable` ``__iter__`` -:class:`Iterator` :class:`Iterable` ``__next__`` ``__iter__`` -:class:`Sized` ``__len__`` -:class:`Callable` ``__call__`` - -:class:`Sequence` :class:`Sized`, ``__getitem__`` ``__contains__``. ``__iter__``, ``__reversed__``. - :class:`Iterable`, ``index``, and ``count`` - :class:`Container` - -:class:`MutableSequence` :class:`Sequence` ``__setitem__`` Inherited Sequence methods and - ``__delitem__``, ``append``, ``reverse``, ``extend``, ``pop``, - and ``insert`` ``remove``, and ``__iadd__`` - -:class:`Set` :class:`Sized`, ``__le__``, ``__lt__``, ``__eq__``, ``__ne__``, - :class:`Iterable`, ``__gt__``, ``__ge__``, ``__and__``, ``__or__`` - :class:`Container` ``__sub__``, ``__xor__``, and ``isdisjoint`` - -:class:`MutableSet` :class:`Set` ``add`` and Inherited Set methods and - ``discard`` ``clear``, ``pop``, ``remove``, ``__ior__``, - ``__iand__``, ``__ixor__``, and ``__isub__`` - -:class:`Mapping` :class:`Sized`, ``__getitem__`` ``__contains__``, ``keys``, ``items``, ``values``, - :class:`Iterable`, ``get``, ``__eq__``, and ``__ne__`` - :class:`Container` - -:class:`MutableMapping` :class:`Mapping` ``__setitem__`` and Inherited Mapping methods and - ``__delitem__`` ``pop``, ``popitem``, ``clear``, ``update``, - and ``setdefault`` - - -:class:`MappingView` :class:`Sized` ``__len__`` -:class:`KeysView` :class:`MappingView`, ``__contains__``, - :class:`Set` ``__iter__`` -:class:`ItemsView` :class:`MappingView`, ``__contains__``, - :class:`Set` ``__iter__`` -:class:`ValuesView` :class:`MappingView` ``__contains__``, ``__iter__`` -========================= ===================== ====================== ==================================================== - -These ABCs allow us to ask classes or instances if they provide -particular functionality, for example:: - - size = None - if isinstance(myvar, collections.Sized): - size = len(myvar) - -Several of the ABCs are also useful as mixins that make it easier to develop -classes supporting container APIs. For example, to write a class supporting -the full :class:`Set` API, it only necessary to supply the three underlying -abstract methods: :meth:`__contains__`, :meth:`__iter__`, and :meth:`__len__`. -The ABC supplies the remaining methods such as :meth:`__and__` and -:meth:`isdisjoint` :: - - class ListBasedSet(collections.Set): - ''' Alternate set implementation favoring space over speed - and not requiring the set elements to be hashable. ''' - def __init__(self, iterable): - self.elements = lst = [] - for value in iterable: - if value not in lst: - lst.append(value) - def __iter__(self): - return iter(self.elements) - def __contains__(self, value): - return value in self.elements - def __len__(self): - return len(self.elements) - - s1 = ListBasedSet('abcdef') - s2 = ListBasedSet('defghi') - overlap = s1 & s2 # The __and__() method is supported automatically - -Notes on using :class:`Set` and :class:`MutableSet` as a mixin: - -(1) - Since some set operations create new sets, the default mixin methods need - a way to create new instances from an iterable. The class constructor is - assumed to have a signature in the form ``ClassName(iterable)``. - That assumption is factored-out to an internal classmethod called - :meth:`_from_iterable` which calls ``cls(iterable)`` to produce a new set. - If the :class:`Set` mixin is being used in a class with a different - constructor signature, you will need to override :meth:`from_iterable` - with a classmethod that can construct new instances from - an iterable argument. - -(2) - To override the comparisons (presumably for speed, as the - semantics are fixed), redefine :meth:`__le__` and - then the other operations will automatically follow suit. - -(3) - The :class:`Set` mixin provides a :meth:`_hash` method to compute a hash value - for the set; however, :meth:`__hash__` is not defined because not all sets - are hashable or immutable. To add set hashabilty using mixins, - inherit from both :meth:`Set` and :meth:`Hashable`, then define - ``__hash__ = Set._hash``. - -.. seealso:: - - * `OrderedSet recipe `_ for an - example built on :class:`MutableSet`. - - * For more about ABCs, see the :mod:`abc` module and :pep:`3119`. +This module implements specialized container datatypes providing alternatives to +Python's general purpose built-in containers, :class:`dict`, :class:`list`, +:class:`set`, and :class:`tuple`. + +===================== ==================================================================== +:func:`namedtuple` factory function for creating tuple subclasses with named fields +:class:`deque` list-like container with fast appends and pops on either end +:class:`Counter` dict subclass for counting hashable objects +:class:`OrderedDict` dict subclass that remembers the order entries were added +:class:`defaultdict` dict subclass that calls a factory function to supply missing values +:class:`UserDict` wrapper around dictionary objects for easier dict subclassing +:class:`UserList` wrapper around list objects for easier list subclassing +:class:`UserString` wrapper around string objects for easier string subclassing +===================== ==================================================================== + +In addition to the concrete container classes, the collections module provides +ABCs (abstract base classes) that can be used to test whether a class provides a +particular interface, for example, whether it is hashable or a mapping. :class:`Counter` objects @@ -551,7 +444,7 @@ :class:`defaultdict` objects support the following method in addition to the standard :class:`dict` operations: - .. method:: defaultdict.__missing__(key) + .. method:: __missing__(key) If the :attr:`default_factory` attribute is ``None``, this raises a :exc:`KeyError` exception with the *key* as argument. @@ -571,7 +464,7 @@ :class:`defaultdict` objects support the following instance variable: - .. attribute:: defaultdict.default_factory + .. attribute:: default_factory This attribute is used by the :meth:`__missing__` method; it is initialized from the first argument to the constructor, if present, or to @@ -712,7 +605,7 @@ def __repr__(self): 'Return a nicely formatted representation string' - return 'Point(x=%r, y=%r)' % self + return self.__class__.__name__ + '(x=%r, y=%r)' % self def _asdict(self): 'Return a new OrderedDict which maps field names to their values' @@ -763,7 +656,7 @@ three additional methods and one attribute. To prevent conflicts with field names, the method and attribute names start with an underscore. -.. method:: somenamedtuple._make(iterable) +.. classmethod:: somenamedtuple._make(iterable) Class method that makes a new instance from an existing sequence or iterable. @@ -891,11 +784,11 @@ .. versionadded:: 3.1 -.. method:: OrderedDict.popitem(last=True) + .. method:: popitem(last=True) - The :meth:`popitem` method for ordered dictionaries returns and removes - a (key, value) pair. The pairs are returned in LIFO order if *last* is - true or FIFO order if false. + The :meth:`popitem` method for ordered dictionaries returns and removes a + (key, value) pair. The pairs are returned in LIFO order if *last* is true + or FIFO order if false. In addition to the usual mapping methods, ordered dictionaries also support reverse iteration using :func:`reversed`. @@ -938,6 +831,18 @@ are deleted. But when new keys are added, the keys are appended to the end and the sort is not maintained. +It is also straight-forward to create an ordered dictionary variant +that the remembers the order the keys were *last* inserted. +If a new entry overwrites an existing entry, the +original insertion position is changed and moved to the end:: + + class LastUpdatedOrderedDict(OrderedDict): + 'Store items is the order the keys were last added' + def __setitem__(self, key, value): + if key in self: + del self[key] + OrderedDict.__setitem__(self, key, value) + :class:`UserDict` objects ------------------------- @@ -956,12 +861,13 @@ initialized with its contents; note that a reference to *initialdata* will not be kept, allowing it be used for other purposes. -In addition to supporting the methods and operations of mappings, -:class:`UserDict` instances provide the following attribute: + In addition to supporting the methods and operations of mappings, + :class:`UserDict` instances provide the following attribute: -.. attribute:: UserDict.data + .. attribute:: data - A real dictionary used to store the contents of the :class:`UserDict` class. + A real dictionary used to store the contents of the :class:`UserDict` + class. @@ -985,13 +891,13 @@ defaulting to the empty list ``[]``. *list* can be any iterable, for example a real Python list or a :class:`UserList` object. -In addition to supporting the methods and operations of mutable sequences, -:class:`UserList` instances provide the following attribute: + In addition to supporting the methods and operations of mutable sequences, + :class:`UserList` instances provide the following attribute: -.. attribute:: UserList.data + .. attribute:: data - A real :class:`list` object used to store the contents of the - :class:`UserList` class. + A real :class:`list` object used to store the contents of the + :class:`UserList` class. **Subclassing requirements:** Subclasses of :class:`UserList` are expect to offer a constructor which can be called with either no arguments or one @@ -1023,3 +929,117 @@ be an instance of :class:`bytes`, :class:`str`, :class:`UserString` (or a subclass) or an arbitrary sequence which can be converted into a string using the built-in :func:`str` function. + + +ABCs - abstract base classes +---------------------------- + +The collections module offers the following ABCs: + +========================= ===================== ====================== ==================================================== +ABC Inherits Abstract Methods Mixin Methods +========================= ===================== ====================== ==================================================== +:class:`Container` ``__contains__`` +:class:`Hashable` ``__hash__`` +:class:`Iterable` ``__iter__`` +:class:`Iterator` :class:`Iterable` ``__next__`` ``__iter__`` +:class:`Sized` ``__len__`` +:class:`Callable` ``__call__`` + +:class:`Sequence` :class:`Sized`, ``__getitem__`` ``__contains__``. ``__iter__``, ``__reversed__``. + :class:`Iterable`, ``index``, and ``count`` + :class:`Container` + +:class:`MutableSequence` :class:`Sequence` ``__setitem__`` Inherited Sequence methods and + ``__delitem__``, ``append``, ``reverse``, ``extend``, ``pop``, + and ``insert`` ``remove``, and ``__iadd__`` + +:class:`Set` :class:`Sized`, ``__le__``, ``__lt__``, ``__eq__``, ``__ne__``, + :class:`Iterable`, ``__gt__``, ``__ge__``, ``__and__``, ``__or__`` + :class:`Container` ``__sub__``, ``__xor__``, and ``isdisjoint`` + +:class:`MutableSet` :class:`Set` ``add`` and Inherited Set methods and + ``discard`` ``clear``, ``pop``, ``remove``, ``__ior__``, + ``__iand__``, ``__ixor__``, and ``__isub__`` + +:class:`Mapping` :class:`Sized`, ``__getitem__`` ``__contains__``, ``keys``, ``items``, ``values``, + :class:`Iterable`, ``get``, ``__eq__``, and ``__ne__`` + :class:`Container` + +:class:`MutableMapping` :class:`Mapping` ``__setitem__`` and Inherited Mapping methods and + ``__delitem__`` ``pop``, ``popitem``, ``clear``, ``update``, + and ``setdefault`` + + +:class:`MappingView` :class:`Sized` ``__len__`` +:class:`KeysView` :class:`MappingView`, ``__contains__``, + :class:`Set` ``__iter__`` +:class:`ItemsView` :class:`MappingView`, ``__contains__``, + :class:`Set` ``__iter__`` +:class:`ValuesView` :class:`MappingView` ``__contains__``, ``__iter__`` +========================= ===================== ====================== ==================================================== + +These ABCs allow us to ask classes or instances if they provide +particular functionality, for example:: + + size = None + if isinstance(myvar, collections.Sized): + size = len(myvar) + +Several of the ABCs are also useful as mixins that make it easier to develop +classes supporting container APIs. For example, to write a class supporting +the full :class:`Set` API, it only necessary to supply the three underlying +abstract methods: :meth:`__contains__`, :meth:`__iter__`, and :meth:`__len__`. +The ABC supplies the remaining methods such as :meth:`__and__` and +:meth:`isdisjoint` :: + + class ListBasedSet(collections.Set): + ''' Alternate set implementation favoring space over speed + and not requiring the set elements to be hashable. ''' + def __init__(self, iterable): + self.elements = lst = [] + for value in iterable: + if value not in lst: + lst.append(value) + def __iter__(self): + return iter(self.elements) + def __contains__(self, value): + return value in self.elements + def __len__(self): + return len(self.elements) + + s1 = ListBasedSet('abcdef') + s2 = ListBasedSet('defghi') + overlap = s1 & s2 # The __and__() method is supported automatically + +Notes on using :class:`Set` and :class:`MutableSet` as a mixin: + +(1) + Since some set operations create new sets, the default mixin methods need + a way to create new instances from an iterable. The class constructor is + assumed to have a signature in the form ``ClassName(iterable)``. + That assumption is factored-out to an internal classmethod called + :meth:`_from_iterable` which calls ``cls(iterable)`` to produce a new set. + If the :class:`Set` mixin is being used in a class with a different + constructor signature, you will need to override :meth:`from_iterable` + with a classmethod that can construct new instances from + an iterable argument. + +(2) + To override the comparisons (presumably for speed, as the + semantics are fixed), redefine :meth:`__le__` and + then the other operations will automatically follow suit. + +(3) + The :class:`Set` mixin provides a :meth:`_hash` method to compute a hash value + for the set; however, :meth:`__hash__` is not defined because not all sets + are hashable or immutable. To add set hashabilty using mixins, + inherit from both :meth:`Set` and :meth:`Hashable`, then define + ``__hash__ = Set._hash``. + +.. seealso:: + + * `OrderedSet recipe `_ for an + example built on :class:`MutableSet`. + + * For more about ABCs, see the :mod:`abc` module and :pep:`3119`. Modified: python/branches/py3k-dtoa/Doc/library/configparser.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/library/configparser.rst (original) +++ python/branches/py3k-dtoa/Doc/library/configparser.rst Sun Aug 8 12:41:24 2010 @@ -15,105 +15,172 @@ single: ini file single: Windows ini file -This module defines the class :class:`ConfigParser`. The :class:`ConfigParser` -class implements a basic configuration file parser language which provides a -structure similar to what you would find on Microsoft Windows INI files. You -can use this to write Python programs which can be customized by end users -easily. +This module provides the classes :class:`RawConfigParser` and +:class:`SafeConfigParser`. They implement a basic configuration file parser +language which provides a structure similar to what you would find in Microsoft +Windows INI files. You can use this to write Python programs which can be +customized by end users easily. .. note:: This library does *not* interpret or write the value-type prefixes used in the Windows Registry extended version of INI syntax. -The configuration file consists of sections, led by a ``[section]`` header and -followed by ``name: value`` entries, with continuations in the style of -:rfc:`822` (see section 3.1.1, "LONG HEADER FIELDS"); ``name=value`` is also -accepted. Note that leading whitespace is removed from values. The optional -values can contain format strings which refer to other values in the same -section, or values in a special ``DEFAULT`` section. Additional defaults can be -provided on initialization and retrieval. Lines beginning with ``'#'`` or -``';'`` are ignored and may be used to provide comments. +A configuration file consists of sections, each led by a ``[section]`` header, +followed by name/value entries separated by a specific string (``=`` or ``:`` by +default). Note that leading whitespace is removed from values. Values can be +ommitted, in which case the key/value delimiter may also be left out. Values +can also span multiple lines, as long as they are indented deeper than the first +line of the value. Depending on the parser's mode, blank lines may be treated +as parts of multiline values or ignored. + +Configuration files may include comments, prefixed by specific characters (``#`` +and ``;`` by default). Comments may appear on their own in an otherwise empty +line, or may be entered in lines holding values or spection names. In the +latter case, they need to be preceded by a whitespace character to be recognized +as a comment. (For backwards compatibility, by default only ``;`` starts an +inline comment, while ``#`` does not.) + +On top of the core functionality, :class:`SafeConfigParser` supports +interpolation. This means values can contain format strings which refer to +other values in the same section, or values in a special ``DEFAULT`` section. +Additional defaults can be provided on initialization. For example:: - [My Section] - foodir: %(dir)s/whatever - dir=frob - long: this value continues - in the next line - -would resolve the ``%(dir)s`` to the value of ``dir`` (``frob`` in this case). -All reference expansions are done on demand. - -Default values can be specified by passing them into the :class:`ConfigParser` -constructor as a dictionary. Additional defaults may be passed into the -:meth:`get` method which will override all others. - -Sections are normally stored in a built-in dictionary. An alternative dictionary -type can be passed to the :class:`ConfigParser` constructor. For example, if a -dictionary type is passed that sorts its keys, the sections will be sorted on -write-back, as will be the keys within each section. + [Paths] + home_dir: /Users + my_dir: %(home_dir)s/lumberjack + my_pictures: %(my_dir)s/Pictures + + [Multiline Values] + chorus: I'm a lumberjack, and I'm okay + I sleep all night and I work all day + + [No Values] + key_without_value + empty string value here = + + [You can use comments] ; after a useful line + ; in an empty line + after: a_value ; here's another comment + inside: a ;comment + multiline ;comment + value! ;comment + + [Sections Can Be Indented] + can_values_be_as_well = True + does_that_mean_anything_special = False + purpose = formatting for readability + multiline_values = are + handled just fine as + long as they are indented + deeper than the first line + of a value + # Did I mention we can indent comments, too? + + +In the example above, :class:`SafeConfigParser` would resolve ``%(home_dir)s`` +to the value of ``home_dir`` (``/Users`` in this case). ``%(my_dir)s`` in +effect would resolve to ``/Users/lumberjack``. All interpolations are done on +demand so keys used in the chain of references do not have to be specified in +any specific order in the configuration file. + +:class:`RawConfigParser` would simply return ``%(my_dir)s/Pictures`` as the +value of ``my_pictures`` and ``%(home_dir)s/lumberjack`` as the value of +``my_dir``. Other features presented in the example are handled in the same +manner by both parsers. + +Default values can be specified by passing them as a dictionary when +constructing the :class:`SafeConfigParser`. + +Sections are normally stored in an :class:`collections.OrderedDict` which +maintains the order of all keys. An alternative dictionary type can be passed +to the :meth:`__init__` method. For example, if a dictionary type is passed +that sorts its keys, the sections will be sorted on write-back, as will be the +keys within each section. -.. class:: RawConfigParser(defaults=None, dict_type=collections.OrderedDict, - allow_no_value=False) +.. class:: RawConfigParser(defaults=None, dict_type=collections.OrderedDict, delimiters=('=', ':'), comment_prefixes=_COMPATIBLE, empty_lines_in_values=True, allow_no_value=False) The basic configuration object. When *defaults* is given, it is initialized - into the dictionary of intrinsic defaults. When *dict_type* is given, it will - be used to create the dictionary objects for the list of sections, for the - options within a section, and for the default values. When *allow_no_value* - is true (default: ``False``), options without values are accepted; the value + into the dictionary of intrinsic defaults. When *dict_type* is given, it + will be used to create the dictionary objects for the list of sections, for + the options within a section, and for the default values. + + When *delimiters* is given, it will be used as the set of substrings that + divide keys from values. When *comment_prefixes* is given, it will be used + as the set of substrings that prefix comments in a line, both for the whole + line and inline comments. For backwards compatibility, the default value for + *comment_prefixes* is a special value that indicates that ``;`` and ``#`` can + start whole line comments while only ``;`` can start inline comments. + + When *empty_lines_in_values* is ``False`` (default: ``True``), each empty + line marks the end of an option. Otherwise, internal empty lines of a + multiline option are kept as part of the value. When *allow_no_value* is + true (default: ``False``), options without values are accepted; the value presented for these is ``None``. - This class does not - support the magical interpolation behavior. + This class does not support the magical interpolation behavior. .. versionchanged:: 3.1 The default *dict_type* is :class:`collections.OrderedDict`. .. versionchanged:: 3.2 - *allow_no_value* was added. + *delimiters*, *comment_prefixes*, *empty_lines_in_values* and + *allow_no_value* were added. -.. class:: ConfigParser(defaults=None, dict_type=collections.OrderedDict, - allow_no_value=False) +.. class:: SafeConfigParser(defaults=None, dict_type=collections.OrderedDict, delimiters=('=', ':'), comment_prefixes=('#', ';'), empty_lines_in_values=True, allow_no_value=False) - Derived class of :class:`RawConfigParser` that implements the magical - interpolation feature and adds optional arguments to the :meth:`get` and - :meth:`items` methods. The values in *defaults* must be appropriate for the - ``%()s`` string interpolation. Note that *__name__* is an intrinsic default; - its value is the section name, and will override any value provided in - *defaults*. + Derived class of :class:`ConfigParser` that implements a sane variant of the + magical interpolation feature. This implementation is more predictable as it + validates the interpolation syntax used within a configuration file. This + class also enables escaping the interpolation character (e.g. a key can have + ``%`` as part of the value by specifying ``%%`` in the file). - All option names used in interpolation will be passed through the - :meth:`optionxform` method just like any other option name reference. For - example, using the default implementation of :meth:`optionxform` (which converts - option names to lower case), the values ``foo %(bar)s`` and ``foo %(BAR)s`` are - equivalent. + Applications that don't require interpolation should use + :class:`RawConfigParser`, otherwise :class:`SafeConfigParser` is the best + option. .. versionchanged:: 3.1 The default *dict_type* is :class:`collections.OrderedDict`. .. versionchanged:: 3.2 - *allow_no_value* was added. + *delimiters*, *comment_prefixes*, *empty_lines_in_values* and + *allow_no_value* were added. -.. class:: SafeConfigParser(defaults=None, dict_type=collections.OrderedDict, - allow_no_value=False) +.. class:: ConfigParser(defaults=None, dict_type=collections.OrderedDict, delimiters=('=', ':'), comment_prefixes=('#', ';'), empty_lines_in_values=True, allow_no_value=False) - Derived class of :class:`ConfigParser` that implements a more-sane variant of - the magical interpolation feature. This implementation is more predictable as - well. New applications should prefer this version if they don't need to be - compatible with older versions of Python. + Derived class of :class:`RawConfigParser` that implements the magical + interpolation feature and adds optional arguments to the :meth:`get` and + :meth:`items` methods. - .. XXX Need to explain what's safer/more predictable about it. + :class:`SafeConfigParser` is generally recommended over this class if you + need interpolation. + + The values in *defaults* must be appropriate for the ``%()s`` string + interpolation. Note that *__name__* is an intrinsic default; its value is + the section name, and will override any value provided in *defaults*. + + All option names used in interpolation will be passed through the + :meth:`optionxform` method just like any other option name reference. For + example, using the default implementation of :meth:`optionxform` (which + converts option names to lower case), the values ``foo %(bar)s`` and ``foo + %(BAR)s`` are equivalent. .. versionchanged:: 3.1 The default *dict_type* is :class:`collections.OrderedDict`. .. versionchanged:: 3.2 - *allow_no_value* was added. + *delimiters*, *comment_prefixes*, *empty_lines_in_values* and + *allow_no_value* were added. + + +.. exception:: Error + + Base class for all other configparser exceptions. .. exception:: NoSectionError @@ -223,25 +290,29 @@ :const:`True`; otherwise return :const:`False`. -.. method:: RawConfigParser.read(filenames) +.. method:: RawConfigParser.read(filenames, encoding=None) Attempt to read and parse a list of filenames, returning a list of filenames - which were successfully parsed. If *filenames* is a string, - it is treated as a single filename. If a file named in *filenames* cannot be - opened, that file will be ignored. This is designed so that you can specify a - list of potential configuration file locations (for example, the current - directory, the user's home directory, and some system-wide directory), and all - existing configuration files in the list will be read. If none of the named - files exist, the :class:`ConfigParser` instance will contain an empty dataset. - An application which requires initial values to be loaded from a file should - load the required file or files using :meth:`readfp` before calling :meth:`read` - for any optional files:: + which were successfully parsed. If *filenames* is a string, it is treated as + a single filename. If a file named in *filenames* cannot be opened, that + file will be ignored. This is designed so that you can specify a list of + potential configuration file locations (for example, the current directory, + the user's home directory, and some system-wide directory), and all existing + configuration files in the list will be read. If none of the named files + exist, the :class:`ConfigParser` instance will contain an empty dataset. An + application which requires initial values to be loaded from a file should + load the required file or files using :meth:`readfp` before calling + :meth:`read` for any optional files:: import configparser, os config = configparser.ConfigParser() config.readfp(open('defaults.cfg')) - config.read(['site.cfg', os.path.expanduser('~/.myapp.cfg')]) + config.read(['site.cfg', os.path.expanduser('~/.myapp.cfg')], encoding='cp1250') + + .. versionadded:: 3.2 + The *encoding* parameter. Previously, all files were read using the + default encoding for :func:`open`. .. method:: RawConfigParser.readfp(fp, filename=None) @@ -295,11 +366,13 @@ interpolation and output to files) can only be achieved using string values. -.. method:: RawConfigParser.write(fileobject) +.. method:: RawConfigParser.write(fileobject, space_around_delimiters=True) Write a representation of the configuration to the specified file object, which must be opened in text mode (accepting strings). This representation - can be parsed by a future :meth:`read` call. + can be parsed by a future :meth:`read` call. If ``space_around_delimiters`` + is ``True`` (the default), delimiters between keys and values are surrounded + by spaces. .. method:: RawConfigParser.remove_option(section, option) @@ -342,21 +415,27 @@ -------------------- The :class:`ConfigParser` class extends some methods of the -:class:`RawConfigParser` interface, adding some optional arguments. +:class:`RawConfigParser` interface, adding some optional arguments. Whenever you +can, consider using :class:`SafeConfigParser` which adds validation and escaping +for the interpolation. .. method:: ConfigParser.get(section, option, raw=False, vars=None) - Get an *option* value for the named *section*. All the ``'%'`` interpolations - are expanded in the return values, based on the defaults passed into the - constructor, as well as the options *vars* provided, unless the *raw* argument - is true. + Get an *option* value for the named *section*. If *vars* is provided, it + must be a dictionary. The *option* is looked up in *vars* (if provided), + *section*, and in *defaults* in that order. + + All the ``'%'`` interpolations are expanded in the return values, unless the + *raw* argument is true. Values for interpolation keys are looked up in the + same manner as the option. .. method:: ConfigParser.items(section, raw=False, vars=None) - Return a list of ``(name, value)`` pairs for each option in the given *section*. - Optional arguments have the same meaning as for the :meth:`get` method. + Return a list of ``(name, value)`` pairs for each option in the given + *section*. Optional arguments have the same meaning as for the :meth:`get` + method. .. _safeconfigparser-objects: @@ -466,8 +545,8 @@ Some configuration files are known to include settings without values, but which otherwise conform to the syntax supported by :mod:`configparser`. The -*allow_no_value* parameter to the constructor can be used to indicate that such -values should be accepted: +*allow_no_value* parameter to the :meth:`__init__` method can be used to +indicate that such values should be accepted: .. doctest:: @@ -476,12 +555,12 @@ >>> sample_config = """ ... [mysqld] - ... user = mysql - ... pid-file = /var/run/mysqld/mysqld.pid - ... skip-external-locking - ... old_passwords = 1 - ... skip-bdb - ... skip-innodb + ... user = mysql + ... pid-file = /var/run/mysqld/mysqld.pid + ... skip-external-locking + ... old_passwords = 1 + ... skip-bdb + ... skip-innodb # we don't need ACID today ... """ >>> config = configparser.RawConfigParser(allow_no_value=True) >>> config.readfp(io.BytesIO(sample_config)) Modified: python/branches/py3k-dtoa/Doc/library/constants.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/library/constants.rst (original) +++ python/branches/py3k-dtoa/Doc/library/constants.rst Sun Aug 8 12:41:24 2010 @@ -3,15 +3,6 @@ A small number of constants live in the built-in namespace. They are: - -.. note:: - - :data:`None`, :data:`False`, :data:`True` and :data:`__debug__` cannot be - reassigned (assignments to them raise :exc:`SyntaxError`), so they can be - considered "true" constants. - -.. XXX False, True, None are keywords too - .. data:: False The false value of the :class:`bool` type. Assignments to ``False`` @@ -40,19 +31,23 @@ .. data:: Ellipsis - The same as ``...``. Special value used mostly in conjunction with extended - slicing syntax for user-defined container data types, as in :: - - .. XXX Someone who understands extended slicing should fill in here. + The same as ``...``. Special value used mostly in conjunction with extended + slicing syntax for user-defined container data types. .. data:: __debug__ This constant is true if Python was not started with an :option:`-O` option. - Assignments to :const:`__debug__` are illegal and raise a :exc:`SyntaxError`. See also the :keyword:`assert` statement. +.. note:: + + The names :data:`None`, :data:`False`, :data:`True` and :data:`__debug__` + cannot be reassigned (assignments to them, even as an attribute name, raise + :exc:`SyntaxError`), so they can be considered "true" constants. + + Constants added by the :mod:`site` module ----------------------------------------- Modified: python/branches/py3k-dtoa/Doc/library/contextlib.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/library/contextlib.rst (original) +++ python/branches/py3k-dtoa/Doc/library/contextlib.rst Sun Aug 8 12:41:24 2010 @@ -12,7 +12,7 @@ Functions provided: -.. function:: contextmanager(func) +.. decorator:: contextmanager This function is a :term:`decorator` that can be used to define a factory function for :keyword:`with` statement context managers, without needing to @@ -57,6 +57,7 @@ .. versionchanged:: 3.2 Use of :class:`ContextDecorator`. + .. function:: closing(thing) Return a context manager that closes *thing* upon completion of the block. This @@ -92,22 +93,25 @@ ``__enter__`` and ``__exit__`` as normal. ``__exit__`` retains its optional exception handling even when used as a decorator. - Example:: + ``ContextDecorator`` is used by :func:`contextmanager`, so you get this + functionality automatically. + + Example of ``ContextDecorator``:: from contextlib import ContextDecorator class mycontext(ContextDecorator): - def __enter__(self): - print('Starting') - return self - - def __exit__(self, *exc): - print('Finishing') - return False + def __enter__(self): + print('Starting') + return self + + def __exit__(self, *exc): + print('Finishing') + return False >>> @mycontext() ... def function(): - ... print('The bit in the middle') + ... print('The bit in the middle') ... >>> function() Starting @@ -115,23 +119,38 @@ Finishing >>> with mycontext(): - ... print('The bit in the middle') + ... print('The bit in the middle') ... Starting The bit in the middle Finishing + This change is just syntactic sugar for any construct of the following form:: + + def f(): + with cm(): + # Do stuff + + ``ContextDecorator`` lets you instead write:: + + @cm() + def f(): + # Do stuff + + It makes it clear that the ``cm`` applies to the whole function, rather than + just a piece of it (and saving an indentation level is nice, too). + Existing context managers that already have a base class can be extended by using ``ContextDecorator`` as a mixin class:: from contextlib import ContextDecorator class mycontext(ContextBaseClass, ContextDecorator): - def __enter__(self): - return self + def __enter__(self): + return self - def __exit__(self, *exc): - return False + def __exit__(self, *exc): + return False .. versionadded:: 3.2 Modified: python/branches/py3k-dtoa/Doc/library/datetime.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/library/datetime.rst (original) +++ python/branches/py3k-dtoa/Doc/library/datetime.rst Sun Aug 8 12:41:24 2010 @@ -265,6 +265,14 @@ | ``abs(t)`` | equivalent to +\ *t* when ``t.days >= 0``, and| | | to -*t* when ``t.days < 0``. (2) | +--------------------------------+-----------------------------------------------+ +| ``str(t)`` | Returns a string in the form | +| | ``[D day[s], ][H]H:MM:SS[.UUUUUU]``, where D | +| | is negative for negative ``t``. (5) | ++--------------------------------+-----------------------------------------------+ +| ``repr(t)`` | Returns a string in the form | +| | ``datetime.timedelta(D[, S[, U]])``, where D | +| | is negative for negative ``t``. (5) | ++--------------------------------+-----------------------------------------------+ Notes: @@ -280,6 +288,16 @@ (4) -*timedelta.max* is not representable as a :class:`timedelta` object. +(5) + String representations of :class:`timedelta` objects are normalized + similarly to their internal representation. This leads to somewhat + unusual results for negative timedeltas. For example: + + >>> timedelta(hours=-5) + datetime.timedelta(-1, 68400) + >>> print(_) + -1 day, 19:00:00 + In addition to the operations listed above :class:`timedelta` objects support certain additions and subtractions with :class:`date` and :class:`datetime` objects (see below). Modified: python/branches/py3k-dtoa/Doc/library/dbm.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/library/dbm.rst (original) +++ python/branches/py3k-dtoa/Doc/library/dbm.rst Sun Aug 8 12:41:24 2010 @@ -5,10 +5,10 @@ :synopsis: Interfaces to various Unix "database" formats. :mod:`dbm` is a generic interface to variants of the DBM database --- - :mod:`dbm.gnu` or :mod:`dbm.ndbm`. If none of these modules is installed, the - slow-but-simple implementation in module :mod:`dbm.dumb` will be used. There - is a `third party interface `_ to - the Oracle Berkely DB. +:mod:`dbm.gnu` or :mod:`dbm.ndbm`. If none of these modules is installed, the +slow-but-simple implementation in module :mod:`dbm.dumb` will be used. There +is a `third party interface `_ to +the Oracle Berkeley DB. .. exception:: error @@ -21,8 +21,8 @@ .. function:: whichdb(filename) This functionattempts to guess which of the several simple database modules - available --- :mod:`dbm.bsd`, :mod:`dbm.gnu`, :mod:`dbm.ndbm` or - :mod:`dbm.dumb` --- should be used to open a given file. + available --- :mod:`dbm.gnu`, :mod:`dbm.ndbm` or :mod:`dbm.dumb` --- should + be used to open a given file. Returns one of the following values: ``None`` if the file can't be opened because it's unreadable or doesn't exist; the empty string (``''``) if the @@ -227,10 +227,9 @@ always stored as bytes. Printing a ``dbm`` object doesn't print the keys and values, and the :meth:`items` and :meth:`values` methods are not supported. -This module can be used with the "classic" ndbm interface, the BSD DB -compatibility interface, or the GNU GDBM compatibility interface. On Unix, the -:program:`configure` script will attempt to locate the appropriate header file -to simplify building this module. +This module can be used with the "classic" ndbm interface or the GNU GDBM +compatibility interface. On Unix, the :program:`configure` script will attempt +to locate the appropriate header file to simplify building this module. .. exception:: error @@ -246,9 +245,7 @@ .. function:: open(filename[, flag[, mode]]) Open a dbm database and return a ``dbm`` object. The *filename* argument is the - name of the database file (without the :file:`.dir` or :file:`.pag` extensions; - note that the BSD DB implementation of the interface will append the extension - :file:`.db` and only create one file). + name of the database file (without the :file:`.dir` or :file:`.pag` extensions). The optional *flag* argument must be one of these values: Modified: python/branches/py3k-dtoa/Doc/library/doctest.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/library/doctest.rst (original) +++ python/branches/py3k-dtoa/Doc/library/doctest.rst Sun Aug 8 12:41:24 2010 @@ -913,18 +913,16 @@ As your collection of doctest'ed modules grows, you'll want a way to run all their doctests systematically. :mod:`doctest` provides two functions that can be used to create :mod:`unittest` test suites from modules and text files -containing doctests. These test suites can then be run using :mod:`unittest` -test runners:: +containing doctests. To integrate with :mod:`unittest` test discovery, include +a :func:`load_tests` function in your test module:: import unittest import doctest - import my_module_with_doctests, and_another + import my_module_with_doctests - suite = unittest.TestSuite() - for mod in my_module_with_doctests, and_another: - suite.addTest(doctest.DocTestSuite(mod)) - runner = unittest.TextTestRunner() - runner.run(suite) + def load_tests(loader, tests, ignore): + tests.addTests(doctest.DocTestSuite(my_module_with_doctests)) + return test There are two main functions for creating :class:`unittest.TestSuite` instances from text files and modules with doctests: @@ -1675,7 +1673,7 @@ .. exception:: DocTestFailure(test, example, got) - An exception thrown by :class:`DocTestRunner` to signal that a doctest example's + An exception raised by :class:`DocTestRunner` to signal that a doctest example's actual output did not match its expected output. The constructor arguments are used to initialize the member variables of the same names. @@ -1699,9 +1697,9 @@ .. exception:: UnexpectedException(test, example, exc_info) - An exception thrown by :class:`DocTestRunner` to signal that a doctest example - raised an unexpected exception. The constructor arguments are used to - initialize the member variables of the same names. + An exception raised by :class:`DocTestRunner` to signal that a doctest + example raised an unexpected exception. The constructor arguments are used + to initialize the member variables of the same names. :exc:`UnexpectedException` defines the following member variables: Modified: python/branches/py3k-dtoa/Doc/library/email.errors.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/library/email.errors.rst (original) +++ python/branches/py3k-dtoa/Doc/library/email.errors.rst Sun Aug 8 12:41:24 2010 @@ -17,7 +17,7 @@ .. exception:: MessageParseError() - This is the base class for exceptions thrown by the :class:`~email.parser.Parser` + This is the base class for exceptions raised by the :class:`~email.parser.Parser` class. It is derived from :exc:`MessageError`. Modified: python/branches/py3k-dtoa/Doc/library/fileinput.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/library/fileinput.rst (original) +++ python/branches/py3k-dtoa/Doc/library/fileinput.rst Sun Aug 8 12:41:24 2010 @@ -24,7 +24,7 @@ All files are opened in text mode by default, but you can override this by specifying the *mode* parameter in the call to :func:`.input` or -:class:`FileInput()`. If an I/O error occurs during opening or reading a file, +:class:`FileInput`. If an I/O error occurs during opening or reading a file, :exc:`IOError` is raised. If ``sys.stdin`` is used more than once, the second and further use will return @@ -54,6 +54,16 @@ during iteration. The parameters to this function will be passed along to the constructor of the :class:`FileInput` class. + The :class:`FileInput` instance can be used as a context manager in the + :keyword:`with` statement. In this example, *input* is closed after the + :keyword:`with` statement is exited, even if an exception occurs:: + + with fileinput.input(files=('spam.txt', 'eggs.txt')) as input: + process(input) + + .. versionchanged:: 3.2 + Can be used as a context manager. + The following functions use the global state created by :func:`fileinput.input`; if there is no active state, :exc:`RuntimeError` is raised. @@ -132,13 +142,23 @@ *filename* and *mode*, and returns an accordingly opened file-like object. You cannot use *inplace* and *openhook* together. - -**Optional in-place filtering:** if the keyword argument ``inplace=1`` is passed -to :func:`fileinput.input` or to the :class:`FileInput` constructor, the file is -moved to a backup file and standard output is directed to the input file (if a -file of the same name as the backup file already exists, it will be replaced -silently). This makes it possible to write a filter that rewrites its input -file in place. If the *backup* parameter is given (typically as + A :class:`FileInput` instance can be used as a context manager in the + :keyword:`with` statement. In this example, *input* is closed after the + :keyword:`with` statement is exited, even if an exception occurs:: + + with FileInput(files=('spam.txt', 'eggs.txt')) as input: + process(input) + + .. versionchanged:: 3.2 + Can be used as a context manager. + + +**Optional in-place filtering:** if the keyword argument ``inplace=True`` is +passed to :func:`fileinput.input` or to the :class:`FileInput` constructor, the +file is moved to a backup file and standard output is directed to the input file +(if a file of the same name as the backup file already exists, it will be +replaced silently). This makes it possible to write a filter that rewrites its +input file in place. If the *backup* parameter is given (typically as ``backup='.'``), it specifies the extension for the backup file, and the backup file remains around; by default, the extension is ``'.bak'`` and it is deleted when the output file is closed. In-place filtering is disabled Modified: python/branches/py3k-dtoa/Doc/library/fnmatch.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/library/fnmatch.rst (original) +++ python/branches/py3k-dtoa/Doc/library/fnmatch.rst Sun Aug 8 12:41:24 2010 @@ -70,6 +70,8 @@ Return the shell-style *pattern* converted to a regular expression. + Be aware there is no way to quote meta-characters. + Example: >>> import fnmatch, re @@ -82,6 +84,13 @@ <_sre.SRE_Match object at 0x...> +.. function:: purge() + + Clear the internal pattern cache. + + .. versionadded:: 3.2 + + .. seealso:: Module :mod:`glob` Modified: python/branches/py3k-dtoa/Doc/library/ftplib.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/library/ftplib.rst (original) +++ python/branches/py3k-dtoa/Doc/library/ftplib.rst Sun Aug 8 12:41:24 2010 @@ -231,9 +231,9 @@ Retrieve a file or directory listing in ASCII transfer mode. *cmd* should be an appropriate ``RETR`` command (see :meth:`retrbinary`) or a command such as ``LIST``, ``NLST`` or ``MLSD`` (usually just the string - ``'LIST'``). The *callback* function is called for each line, with the - trailing CRLF stripped. The default *callback* prints the line to - ``sys.stdout``. + ``'LIST'``). The *callback* function is called for each line with a + string argument containing the line with the trailing CRLF stripped. + The default *callback* prints the line to ``sys.stdout``. .. method:: FTP.set_pasv(boolean) Modified: python/branches/py3k-dtoa/Doc/library/functions.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/library/functions.rst (original) +++ python/branches/py3k-dtoa/Doc/library/functions.rst Sun Aug 8 12:41:24 2010 @@ -335,6 +335,9 @@ returns the current global and local dictionary, respectively, which may be useful to pass around for use by :func:`eval` or :func:`exec`. + See :func:`ast.literal_eval` for a function that can safely evaluate strings + with expressions containing only literals. + .. function:: exec(object[, globals[, locals]]) @@ -676,7 +679,7 @@ :meth:`__index__` method that returns an integer. -.. function:: open(file, mode='r', buffering=None, encoding=None, errors=None, newline=None, closefd=True) +.. function:: open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True) Open *file* and return a corresponding stream. If the file cannot be opened, an :exc:`IOError` is raised. @@ -844,7 +847,7 @@ *fget* is a function for getting an attribute value, likewise *fset* is a function for setting, and *fdel* a function for del'ing, an attribute. Typical - use is to define a managed attribute x:: + use is to define a managed attribute ``x``:: class C(object): def __init__(self): @@ -858,6 +861,9 @@ del self._x x = property(getx, setx, delx, "I'm the 'x' property.") + If then *c* is an instance of *C*, ``c.x`` will invoke the getter, + ``c.x = value`` will invoke the setter and ``del c.x`` the deleter. + If given, *doc* will be the docstring of the property attribute. Otherwise, the property will copy *fget*'s docstring (if it exists). This makes it possible to create read-only properties easily using :func:`property` as a :term:`decorator`:: @@ -969,6 +975,13 @@ The return value is an integer if called with one argument, otherwise of the same type as *x*. + .. note:: + + The behavior of :func:`round` for floats can be surprising: for example, + ``round(2.675, 2)`` gives ``2.67`` instead of the expected ``2.68``. + This is not a bug: it's a result of the fact that most decimal fractions + can't be represented exactly as a float. See :ref:`tut-fp-issues` for + more information. .. function:: set([iterable]) :noindex: Modified: python/branches/py3k-dtoa/Doc/library/functools.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/library/functools.rst (original) +++ python/branches/py3k-dtoa/Doc/library/functools.rst Sun Aug 8 12:41:24 2010 @@ -37,7 +37,61 @@ .. versionadded:: 3.2 -.. function:: total_ordering(cls) +.. decorator:: lfu_cache(maxsize) + + Decorator to wrap a function with a memoizing callable that saves up to the + *maxsize* most frequent calls. It can save time when an expensive or I/O + bound function is periodically called with the same arguments. + + The *maxsize* parameter defaults to 100. Since a dictionary is used to cache + results, the positional and keyword arguments to the function must be + hashable. + + The wrapped function is instrumented with two attributes, :attr:`hits` + and :attr:`misses` which count the number of successful or unsuccessful + cache lookups. These statistics are helpful for tuning the *maxsize* + parameter and for measuring the cache's effectiveness. + + The wrapped function also has a :attr:`clear` attribute which can be + called (with no arguments) to clear the cache. + + A `LFU (least frequently used) cache + `_ + is indicated when the pattern of calls does not change over time, when + more the most common calls already seen are the best predictors of the + most common upcoming calls (for example, a phonelist of popular + help-lines may have access patterns that persist over time). + + .. versionadded:: 3.2 + +.. decorator:: lru_cache(maxsize) + + Decorator to wrap a function with a memoizing callable that saves up to the + *maxsize* most recent calls. It can save time when an expensive or I/O bound + function is periodically called with the same arguments. + + The *maxsize* parameter defaults to 100. Since a dictionary is used to cache + results, the positional and keyword arguments to the function must be + hashable. + + The wrapped function is instrumented with two attributes, :attr:`hits` + and :attr:`misses` which count the number of successful or unsuccessful + cache lookups. These statistics are helpful for tuning the *maxsize* + parameter and for measuring the cache's effectiveness. + + The wrapped function also has a :attr:`clear` attribute which can be + called (with no arguments) to clear the cache. + + A `LRU (least recently used) cache + `_ + is indicated when the pattern of calls changes over time, such as + when more recent calls are the best predictors of upcoming calls + (for example, the most popular articles on a news server tend to + change every day). + + .. versionadded:: 3.2 + +.. decorator:: total_ordering Given a class defining one or more rich comparison ordering methods, this class decorator supplies the rest. This simplifies the effort involved @@ -111,9 +165,9 @@ attributes of the wrapper function are updated with the corresponding attributes from the original function. The default values for these arguments are the module level constants *WRAPPER_ASSIGNMENTS* (which assigns to the wrapper - function's *__name__*, *__module__* and *__doc__*, the documentation string) and - *WRAPPER_UPDATES* (which updates the wrapper function's *__dict__*, i.e. the - instance dictionary). + function's *__name__*, *__module__*, *__annotations__* and *__doc__*, the + documentation string) and *WRAPPER_UPDATES* (which updates the wrapper + function's *__dict__*, i.e. the instance dictionary). The main intended use for this function is in :term:`decorator` functions which wrap the decorated function and return the wrapper. If the wrapper function is @@ -122,7 +176,7 @@ than helpful. -.. function:: wraps(wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES) +.. decorator:: wraps(wrapped, assigned=WRAPPER_ASSIGNMENTS, updated=WRAPPER_UPDATES) This is a convenience function for invoking ``partial(update_wrapper, wrapped=wrapped, assigned=assigned, updated=updated)`` as a function decorator Modified: python/branches/py3k-dtoa/Doc/library/heapq.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/library/heapq.rst (original) +++ python/branches/py3k-dtoa/Doc/library/heapq.rst Sun Aug 8 12:41:24 2010 @@ -6,6 +6,7 @@ .. moduleauthor:: Kevin O'Connor .. sectionauthor:: Guido van Rossum .. sectionauthor:: Fran??ois Pinard +.. sectionauthor:: Raymond Hettinger This module provides an implementation of the heap queue algorithm, also known as the priority queue algorithm. @@ -138,6 +139,67 @@ functions. +Priority Queue Implementation Notes +----------------------------------- + +A `priority queue `_ is common use +for a heap, and it presents several implementation challenges: + +* Sort stability: how do you get two tasks with equal priorities to be returned + in the order they were originally added? + +* Tuple comparison breaks for (priority, task) pairs if the priorities are equal + and the tasks do not have a default comparison order. + +* If the priority of a task changes, how do you move it to a new position in + the heap? + +* Or if a pending task needs to be deleted, how do you find it and remove it + from the queue? + +A solution to the first two challenges is to store entries as 3-element list +including the priority, an entry count, and the task. The entry count serves as +a tie-breaker so that two tasks with the same priority are returned in the order +they were added. And since no two entry counts are the same, the tuple +comparison will never attempt to directly compare two tasks. + +The remaining challenges revolve around finding a pending task and making +changes to its priority or removing it entirely. Finding a task can be done +with a dictionary pointing to an entry in the queue. + +Removing the entry or changing its priority is more difficult because it would +break the heap structure invariants. So, a possible solution is to mark an +entry as invalid and optionally add a new entry with the revised priority:: + + pq = [] # the priority queue list + counter = itertools.count(1) # unique sequence count + task_finder = {} # mapping of tasks to entries + INVALID = 0 # mark an entry as deleted + + def add_task(priority, task, count=None): + if count is None: + count = next(counter) + entry = [priority, count, task] + task_finder[task] = entry + heappush(pq, entry) + + def get_top_priority(): + while True: + priority, count, task = heappop(pq) + del task_finder[task] + if count is not INVALID: + return task + + def delete_task(task): + entry = task_finder[task] + entry[1] = INVALID + + def reprioritize(priority, task): + entry = task_finder[task] + add_task(priority, task, entry[1]) + entry[1] = INVALID + + Theory ------ Modified: python/branches/py3k-dtoa/Doc/library/html.parser.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/library/html.parser.rst (original) +++ python/branches/py3k-dtoa/Doc/library/html.parser.rst Sun Aug 8 12:41:24 2010 @@ -134,10 +134,18 @@ .. method:: HTMLParser.handle_decl(decl) - Method called when an SGML declaration is read by the parser. The *decl* - parameter will be the entire contents of the declaration inside the ```` markup. It is intended to be overridden by a derived class; the base - class implementation does nothing. + Method called when an SGML ``doctype`` declaration is read by the parser. + The *decl* parameter will be the entire contents of the declaration inside + the ```` markup. It is intended to be overridden by a derived class; + the base class implementation does nothing. + + +.. method:: HTMLParser.unknown_decl(data) + + Method called when an unrecognized SGML declaration is read by the parser. + The *data* parameter will be the entire contents of the declaration inside + the ```` markup. It is sometimes useful to be be overridden by a + derived class; the base class implementation raises an :exc:`HTMLParseError`. .. method:: HTMLParser.handle_pi(data) Modified: python/branches/py3k-dtoa/Doc/library/http.client.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/library/http.client.rst (original) +++ python/branches/py3k-dtoa/Doc/library/http.client.rst Sun Aug 8 12:41:24 2010 @@ -465,8 +465,10 @@ .. method:: HTTPResponse.getheader(name, default=None) - Get the contents of the header *name*, or *default* if there is no matching - header. + Return the value of the header *name*, or *default* if there is no header + matching *name*. If there is more than one header with the name *name*, + return all of the values joined by ', '. If 'default' is any iterable other + than a single string, its elements are similarly returned joined by commas. .. method:: HTTPResponse.getheaders() Modified: python/branches/py3k-dtoa/Doc/library/http.cookiejar.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/library/http.cookiejar.rst (original) +++ python/branches/py3k-dtoa/Doc/library/http.cookiejar.rst Sun Aug 8 12:41:24 2010 @@ -108,10 +108,6 @@ :mod:`http.cookiejar` and :mod:`http.cookies` modules do not depend on each other. - http://wwwsearch.sourceforge.net/mechanize/ - Extensions to this module, including a class for reading Microsoft Internet - Explorer cookies on Windows. - http://wp.netscape.com/newsref/std/cookie_spec.html The specification of the original Netscape cookie protocol. Though this is still the dominant protocol, the 'Netscape cookie protocol' implemented by all @@ -292,11 +288,8 @@ FileCookieJar subclasses and co-operation with web browsers ----------------------------------------------------------- -The following :class:`CookieJar` subclasses are provided for reading and writing -. Further :class:`CookieJar` subclasses, including one that reads Microsoft -Internet Explorer cookies, are available at -http://wwwsearch.sourceforge.net/mechanize/. - +The following :class:`CookieJar` subclasses are provided for reading and +writing . .. class:: MozillaCookieJar(filename, delayload=None, policy=None) Modified: python/branches/py3k-dtoa/Doc/library/importlib.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/library/importlib.rst (original) +++ python/branches/py3k-dtoa/Doc/library/importlib.rst Sun Aug 8 12:41:24 2010 @@ -228,7 +228,7 @@ * :meth:`ResourceLoader.get_data` * :meth:`ExecutionLoader.get_filename` - Implement to only return the path to the source file; sourceless + Should only return the path to the source file; sourceless loading is not supported. The abstract methods defined by this class are to add optional bytecode @@ -469,7 +469,7 @@ This module contains the various objects that help in the construction of an :term:`importer`. -.. function:: module_for_loader(method) +.. decorator:: module_for_loader A :term:`decorator` for a :term:`loader` method, to handle selecting the proper @@ -494,7 +494,7 @@ Use of this decorator handles all the details of which module object a loader should initialize as specified by :pep:`302`. -.. function:: set_loader(fxn) +.. decorator:: set_loader A :term:`decorator` for a :term:`loader` method, to set the :attr:`__loader__` @@ -502,7 +502,7 @@ does nothing. It is assumed that the first positional argument to the wrapped method is what :attr:`__loader__` should be set to. -.. function:: set_package(fxn) +.. decorator:: set_package A :term:`decorator` for a :term:`loader` to set the :attr:`__package__` attribute on the module returned by the loader. If :attr:`__package__` is Modified: python/branches/py3k-dtoa/Doc/library/io.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/library/io.rst (original) +++ python/branches/py3k-dtoa/Doc/library/io.rst Sun Aug 8 12:41:24 2010 @@ -17,7 +17,7 @@ At the top of the I/O hierarchy is the abstract base class :class:`IOBase`. It defines the basic interface to a stream. Note, however, that there is no separation between reading and writing to streams; implementations are allowed -to throw an :exc:`IOError` if they do not support a given operation. +to raise an :exc:`IOError` if they do not support a given operation. Extending :class:`IOBase` is :class:`RawIOBase` which deals simply with the reading and writing of raw bytes to a stream. :class:`FileIO` subclasses @@ -61,8 +61,8 @@ Open *file* and return a corresponding stream. If the file cannot be opened, an :exc:`IOError` is raised. - *file* is either a string or bytes object giving the name (and the path if - the file isn't in the current working directory) of the file to be opened or + *file* is either a string or bytes object giving the pathname (absolute or + relative to the current working directory) of the file to be opened or an integer file descriptor of the file to be wrapped. (If a file descriptor is given, it is closed when the returned I/O object is closed, unless *closefd* is set to ``False``.) Modified: python/branches/py3k-dtoa/Doc/library/itertools.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/library/itertools.rst (original) +++ python/branches/py3k-dtoa/Doc/library/itertools.rst Sun Aug 8 12:41:24 2010 @@ -99,7 +99,7 @@ yield element -.. function:: itertools.chain.from_iterable(iterable) +.. classmethod:: chain.from_iterable(iterable) Alternate constructor for :func:`chain`. Gets chained inputs from a single iterable argument that is evaluated lazily. Equivalent to:: @@ -653,6 +653,12 @@ pending -= 1 nexts = cycle(islice(nexts, pending)) + def partition(pred, iterable): + 'Use a predicate to partition entries into false entries and true entries' + # partition(is_odd, range(10)) --> 0 2 4 6 8 and 1 3 5 7 9 + t1, t2 = tee(iterable) + return filterfalse(pred, t1), filter(pred, t2) + def powerset(iterable): "powerset([1,2,3]) --> () (1,) (2,) (3,) (1,2) (1,3) (2,3) (1,2,3)" s = list(iterable) Modified: python/branches/py3k-dtoa/Doc/library/linecache.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/library/linecache.rst (original) +++ python/branches/py3k-dtoa/Doc/library/linecache.rst Sun Aug 8 12:41:24 2010 @@ -16,7 +16,7 @@ .. function:: getline(filename, lineno, module_globals=None) - Get line *lineno* from file named *filename*. This function will never throw an + Get line *lineno* from file named *filename*. This function will never raise an exception --- it will return ``''`` on errors (the terminating newline character will be included for lines that are found). Modified: python/branches/py3k-dtoa/Doc/library/locale.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/library/locale.rst (original) +++ python/branches/py3k-dtoa/Doc/library/locale.rst Sun Aug 8 12:41:24 2010 @@ -244,10 +244,6 @@ specified, and therefore you should not assume knowledge of it on different systems. - .. data:: ERA_YEAR - - Get the year in the relevant era of the locale. - .. data:: ERA_D_T_FMT Get a format string for :func:`strftime` to represent dates and times in a Modified: python/branches/py3k-dtoa/Doc/library/math.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/library/math.rst (original) +++ python/branches/py3k-dtoa/Doc/library/math.rst Sun Aug 8 12:41:24 2010 @@ -97,15 +97,23 @@ `_\. +.. function:: isfinite(x) + + Return ``True`` if *x* is neither an infinity nor a NaN, and + ``False`` otherwise. (Note that ``0.0`` *is* considered finite.) + + .. versionadded:: 3.2 + + .. function:: isinf(x) - Check if the float *x* is positive or negative infinity. + Return ``True`` if *x* is a positive or negative infinity, and + ``False`` otherwise. .. function:: isnan(x) - Check if the float *x* is a NaN (not a number). For more information - on NaNs, see the IEEE 754 standards. + Return ``True`` if *x* is a NaN (not a number), and ``False`` otherwise. .. function:: ldexp(x, i) Modified: python/branches/py3k-dtoa/Doc/library/mmap.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/library/mmap.rst (original) +++ python/branches/py3k-dtoa/Doc/library/mmap.rst Sun Aug 8 12:41:24 2010 @@ -112,6 +112,18 @@ map.close() + :class:`mmap` can also be used as a context manager in a :keyword:`with` + statement.:: + + import mmap + + with mmap.mmap(-1, 13) as map: + map.write("Hello world!") + + .. versionadded:: 3.2 + Context manager support. + + The next example demonstrates how to create an anonymous map and exchange data between the parent and child processes:: @@ -132,13 +144,19 @@ Memory-mapped file objects support the following methods: - .. method:: close() Close the file. Subsequent calls to other methods of the object will result in an exception being raised. + .. attribute:: closed + + True if the file is closed. + + .. versionadded:: 3.2 + + .. method:: find(sub[, start[, end]]) Returns the lowest index in the object where the subsequence *sub* is @@ -166,7 +184,7 @@ Copy the *count* bytes starting at offset *src* to the destination index *dest*. If the mmap was created with :const:`ACCESS_READ`, then calls to - move will throw a :exc:`TypeError` exception. + move will raise a :exc:`TypeError` exception. .. method:: read(num) @@ -192,7 +210,7 @@ Resizes the map and the underlying file, if any. If the mmap was created with :const:`ACCESS_READ` or :const:`ACCESS_COPY`, resizing the map will - throw a :exc:`TypeError` exception. + raise a :exc:`TypeError` exception. .. method:: rfind(sub[, start[, end]]) @@ -227,7 +245,7 @@ Write the bytes in *bytes* into memory at the current position of the file pointer; the file position is updated to point after the bytes that were written. If the mmap was created with :const:`ACCESS_READ`, then - writing to it will throw a :exc:`TypeError` exception. + writing to it will raise a :exc:`TypeError` exception. .. method:: write_byte(byte) @@ -235,6 +253,4 @@ Write the the integer *byte* into memory at the current position of the file pointer; the file position is advanced by ``1``. If the mmap was created with :const:`ACCESS_READ`, then writing to it will - throw a :exc:`TypeError` exception. - - + raise a :exc:`TypeError` exception. Modified: python/branches/py3k-dtoa/Doc/library/multiprocessing.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/library/multiprocessing.rst (original) +++ python/branches/py3k-dtoa/Doc/library/multiprocessing.rst Sun Aug 8 12:41:24 2010 @@ -2228,8 +2228,8 @@ An example of how a pool of worker processes can each run a -:class:`SimpleHTTPServer.HttpServer` instance while sharing a single listening -socket. +:class:`~http.server.SimpleHTTPRequestHandler` instance while sharing a single +listening socket. .. literalinclude:: ../includes/mp_webserver.py Modified: python/branches/py3k-dtoa/Doc/library/optparse.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/library/optparse.rst (original) +++ python/branches/py3k-dtoa/Doc/library/optparse.rst Sun Aug 8 12:41:24 2010 @@ -1234,8 +1234,9 @@ the list of arguments to process (default: ``sys.argv[1:]``) ``values`` - object to store option arguments in (default: a new instance of - :class:`optparse.Values`) + a :class:`optparse.Values` object to store option arguments in (default: a + new instance of :class:`Values`) -- if you give an existing object, the + option defaults will not be initialized on it and the return values are Modified: python/branches/py3k-dtoa/Doc/library/os.path.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/library/os.path.rst (original) +++ python/branches/py3k-dtoa/Doc/library/os.path.rst Sun Aug 8 12:41:24 2010 @@ -207,7 +207,9 @@ .. function:: normpath(path) Normalize a pathname. This collapses redundant separators and up-level - references so that ``A//B``, ``A/./B`` and ``A/foo/../B`` all become ``A/B``. + references so that ``A//B``, ``A/B/``, ``A/./B`` and ``A/foo/../B`` all become + ``A/B``. + It does not normalize the case (use :func:`normcase` for that). On Windows, it converts forward slashes to backward slashes. It should be understood that this may change the meaning of the path if it contains symbolic links! @@ -231,11 +233,18 @@ .. function:: samefile(path1, path2) - Return ``True`` if both pathname arguments refer to the same file or directory - (as indicated by device number and i-node number). Raise an exception if a - :func:`os.stat` call on either pathname fails. + Return ``True`` if both pathname arguments refer to the same file or directory. + On Unix, this is determined by the device number and i-node number and raises an + exception if a :func:`os.stat` call on either pathname fails. + + On Windows, two files are the same if they resolve to the same final path + name using the Windows API call GetFinalPathNameByHandle. This function + raises an exception if handles cannot be obtained to either file. - Availability: Unix. + Availability: Windows, Unix. + + .. versionchanged:: 3.2 + Added Windows support. .. function:: sameopenfile(fp1, fp2) Modified: python/branches/py3k-dtoa/Doc/library/os.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/library/os.rst (original) +++ python/branches/py3k-dtoa/Doc/library/os.rst Sun Aug 8 12:41:24 2010 @@ -723,7 +723,7 @@ This function is intended for low-level I/O. For normal usage, use the built-in function :func:`open`, which returns a "file object" with - :meth:`~file.read` and :meth:`~file.wprite` methods (and many more). To + :meth:`~file.read` and :meth:`~file.write` methods (and many more). To wrap a file descriptor in a "file object", use :func:`fdopen`. @@ -1049,10 +1049,10 @@ Availability: Unix. -.. function:: listdir(path) +.. function:: listdir(path='.') Return a list containing the names of the entries in the directory given by - *path*. The list is in arbitrary order. It does not include the special + *path* (default: ``'.'``). The list is in arbitrary order. It does not include the special entries ``'.'`` and ``'..'`` even if they are present in the directory. This function can be called with a bytes or string argument, and returns @@ -1060,12 +1060,16 @@ Availability: Unix, Windows. + .. versionchanged:: 3.2 + The *path* parameter became optional. .. function:: lstat(path) Like :func:`stat`, but do not follow symbolic links. This is an alias for - :func:`stat` on platforms that do not support symbolic links, such as - Windows. + :func:`stat` on platforms that do not support symbolic links. + + .. versionchanged:: 3.2 + Added support for Windows 6.0 (Vista) symbolic links. .. function:: mkfifo(path[, mode]) @@ -1181,7 +1185,10 @@ and the call may raise an UnicodeDecodeError. If the *path* is a bytes object, the result will be a bytes object. - Availability: Unix. + Availability: Unix, Windows + + .. versionchanged:: 3.2 + Added support for Windows 6.0 (Vista) symbolic links. .. function:: remove(path) @@ -1343,7 +1350,27 @@ Create a symbolic link pointing to *source* named *link_name*. - Availability: Unix. + On Windows, symlink version takes an additional, optional parameter, + *target_is_directory*, which defaults to False. + + symlink(source, link_name, target_is_directory=False) + + On Windows, a symlink represents a file or a directory, and does not + morph to the target dynamically. For this reason, when creating a + symlink on Windows, if the target is not already present, the symlink + will default to being a file symlink. If *target_is_directory* is set to + True, the symlink will be created as a directory symlink. This + parameter is ignored if the target exists (and the symlink is created + with the same type as the target). + + Symbolic link support was introduced in Windows 6.0 (Vista). *symlink* + will raise a NotImplementedError on Windows versions earlier than 6.0. The + SeCreateSymbolicLinkPrivilege is required in order to create symlinks. + + Availability: Unix, Windows + + .. versionchanged:: 3.2 + Added support for Windows 6.0 (Vista) symbolic links. .. function:: unlink(path) @@ -1896,8 +1923,9 @@ The :mod:`subprocess` module provides more powerful facilities for spawning new processes and retrieving their results; using that module is preferable to using - this function. Use the :mod:`subprocess` module. Check especially the - :ref:`subprocess-replacements` section. + this function. See the + :ref:`subprocess-replacements` section in the :mod:`subprocess` documentation + for some helpful recipes. Availability: Unix, Windows. Modified: python/branches/py3k-dtoa/Doc/library/parser.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/library/parser.rst (original) +++ python/branches/py3k-dtoa/Doc/library/parser.rst Sun Aug 8 12:41:24 2010 @@ -114,7 +114,7 @@ The :func:`expr` function parses the parameter *source* as if it were an input to ``compile(source, 'file.py', 'eval')``. If the parse succeeds, an ST object is created to hold the internal parse tree representation, otherwise an - appropriate exception is thrown. + appropriate exception is raised. .. function:: suite(source) @@ -122,7 +122,7 @@ The :func:`suite` function parses the parameter *source* as if it were an input to ``compile(source, 'file.py', 'exec')``. If the parse succeeds, an ST object is created to hold the internal parse tree representation, otherwise an - appropriate exception is thrown. + appropriate exception is raised. .. function:: sequence2st(sequence) @@ -132,9 +132,9 @@ to the Python grammar and all nodes are valid node types in the host version of Python, an ST object is created from the internal representation and returned to the called. If there is a problem creating the internal representation, or - if the tree cannot be validated, a :exc:`ParserError` exception is thrown. An + if the tree cannot be validated, a :exc:`ParserError` exception is raised. An ST object created this way should not be assumed to compile correctly; normal - exceptions thrown by compilation may still be initiated when the ST object is + exceptions raised by compilation may still be initiated when the ST object is passed to :func:`compilest`. This may indicate problems not related to syntax (such as a :exc:`MemoryError` exception), but may also be due to constructs such as the result of parsing ``del f(0)``, which escapes the Python parser but is @@ -259,8 +259,8 @@ .. exception:: ParserError Exception raised when a failure occurs within the parser module. This is - generally produced for validation failures rather than the built in - :exc:`SyntaxError` thrown during normal parsing. The exception argument is + generally produced for validation failures rather than the built-in + :exc:`SyntaxError` raised during normal parsing. The exception argument is either a string describing the reason of the failure or a tuple containing a sequence causing the failure from a parse tree passed to :func:`sequence2st` and an explanatory string. Calls to :func:`sequence2st` need to be able to @@ -268,7 +268,7 @@ will only need to be aware of the simple string values. Note that the functions :func:`compilest`, :func:`expr`, and :func:`suite` may -throw exceptions which are normally thrown by the parsing and compilation +raise exceptions which are normally thrown by the parsing and compilation process. These include the built in exceptions :exc:`MemoryError`, :exc:`OverflowError`, :exc:`SyntaxError`, and :exc:`SystemError`. In these cases, these exceptions carry all the meaning normally associated with them. Modified: python/branches/py3k-dtoa/Doc/library/pdb.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/library/pdb.rst (original) +++ python/branches/py3k-dtoa/Doc/library/pdb.rst Sun Aug 8 12:41:24 2010 @@ -20,7 +20,7 @@ module: bdb module: cmd -The debugger is extensible --- it is actually defined as the class :class:`Pdb`. +The debugger is extensible -- it is actually defined as the class :class:`Pdb`. This is currently undocumented but easily understood by reading the source. The extension interface uses the modules :mod:`bdb` and :mod:`cmd`. @@ -44,19 +44,23 @@ python3 -m pdb myscript.py When invoked as a script, pdb will automatically enter post-mortem debugging if -the program being debugged exits abnormally. After post-mortem debugging (or -after normal exit of the program), pdb will restart the program. Automatic +the program being debugged exits abnormally. After post-mortem debugging (or +after normal exit of the program), pdb will restart the program. Automatic restarting preserves pdb's state (such as breakpoints) and in most cases is more useful than quitting the debugger upon program's exit. +.. versionadded:: 3.2 + :file:`pdb.py` now accepts a ``-c`` option that executes commands as if given + in a :file:`.pdbrc` file, see :ref:`debugger-commands`. + The typical usage to break into the debugger from a running program is to insert :: import pdb; pdb.set_trace() at the location you want to break into the debugger. You can then step through -the code following this statement, and continue running without the debugger using -the ``c`` command. +the code following this statement, and continue running without the debugger +using the :pdbcmd:`continue` command. The typical usage to inspect a crashed program is:: @@ -81,27 +85,29 @@ .. function:: run(statement, globals=None, locals=None) - Execute the *statement* (given as a string) under debugger control. The - debugger prompt appears before any code is executed; you can set breakpoints and - type ``continue``, or you can step through the statement using ``step`` or - ``next`` (all these commands are explained below). The optional *globals* and - *locals* arguments specify the environment in which the code is executed; by - default the dictionary of the module :mod:`__main__` is used. (See the - explanation of the built-in :func:`exec` or :func:`eval` functions.) + Execute the *statement* (given as a string or a code object) under debugger + control. The debugger prompt appears before any code is executed; you can + set breakpoints and type :pdbcmd:`continue`, or you can step through the + statement using :pdbcmd:`step` or :pdbcmd:`next` (all these commands are + explained below). The optional *globals* and *locals* arguments specify the + environment in which the code is executed; by default the dictionary of the + module :mod:`__main__` is used. (See the explanation of the built-in + :func:`exec` or :func:`eval` functions.) .. function:: runeval(expression, globals=None, locals=None) - Evaluate the *expression* (given as a string) under debugger control. When - :func:`runeval` returns, it returns the value of the expression. Otherwise this - function is similar to :func:`run`. + Evaluate the *expression* (given as a string or a code object) under debugger + control. When :func:`runeval` returns, it returns the value of the + expression. Otherwise this function is similar to :func:`run`. .. function:: runcall(function, *args, **kwds) - Call the *function* (a function or method object, not a string) with the given - arguments. When :func:`runcall` returns, it returns whatever the function call - returned. The debugger prompt appears as soon as the function is entered. + Call the *function* (a function or method object, not a string) with the + given arguments. When :func:`runcall` returns, it returns whatever the + function call returned. The debugger prompt appears as soon as the function + is entered. .. function:: set_trace() @@ -160,16 +166,17 @@ Debugger Commands ================= -The debugger recognizes the following commands. Most commands can be -abbreviated to one or two letters; e.g. ``h(elp)`` means that either ``h`` or -``help`` can be used to enter the help command (but not ``he`` or ``hel``, nor -``H`` or ``Help`` or ``HELP``). Arguments to commands must be separated by -whitespace (spaces or tabs). Optional arguments are enclosed in square brackets -(``[]``) in the command syntax; the square brackets must not be typed. -Alternatives in the command syntax are separated by a vertical bar (``|``). +The commands recognized by the debugger are listed below. Most commands can be +abbreviated to one or two letters as indicated; e.g. ``h(elp)`` means that +either ``h`` or ``help`` can be used to enter the help command (but not ``he`` +or ``hel``, nor ``H`` or ``Help`` or ``HELP``). Arguments to commands must be +separated by whitespace (spaces or tabs). Optional arguments are enclosed in +square brackets (``[]``) in the command syntax; the square brackets must not be +typed. Alternatives in the command syntax are separated by a vertical bar +(``|``). Entering a blank line repeats the last command entered. Exception: if the last -command was a ``list`` command, the next 11 lines are listed. +command was a :pdbcmd:`list` command, the next 11 lines are listed. Commands that the debugger doesn't recognize are assumed to be Python statements and are executed in the context of the program being debugged. Python @@ -179,93 +186,111 @@ statement, the exception name is printed but the debugger's state is not changed. +The debugger supports :ref:`aliases `. Aliases can have +parameters which allows one a certain level of adaptability to the context under +examination. + Multiple commands may be entered on a single line, separated by ``;;``. (A single ``;`` is not used as it is the separator for multiple commands in a line -that is passed to the Python parser.) No intelligence is applied to separating +that is passed to the Python parser.) No intelligence is applied to separating the commands; the input is split at the first ``;;`` pair, even if it is in the middle of a quoted string. -The debugger supports aliases. Aliases can have parameters which allows one a -certain level of adaptability to the context under examination. - .. index:: pair: .pdbrc; file triple: debugger; configuration; file -If a file :file:`.pdbrc` exists in the user's home directory or in the current +If a file :file:`.pdbrc` exists in the user's home directory or in the current directory, it is read in and executed as if it had been typed at the debugger -prompt. This is particularly useful for aliases. If both files exist, the one +prompt. This is particularly useful for aliases. If both files exist, the one in the home directory is read first and aliases defined there can be overridden by the local file. -h(elp) [*command*] +.. versionchanged:: 3.2 + :file:`.pdbrc` can now contain commands that continue debugging, such as + :pdbcmd:`continue` or :pdbcmd:`next`. Previously, these commands had no + effect. + + +.. pdbcommand:: h(elp) [command] + Without argument, print the list of available commands. With a *command* as argument, print help about that command. ``help pdb`` displays the full - documentation file; if the environment variable :envvar:`PAGER` is defined, the - file is piped through that command instead. Since the *command* argument must - be an identifier, ``help exec`` must be entered to get help on the ``!`` - command. + documentation (the docstring of the :mod:`pdb` module). Since the *command* + argument must be an identifier, ``help exec`` must be entered to get help on + the ``!`` command. + +.. pdbcommand:: w(here) -w(here) Print a stack trace, with the most recent frame at the bottom. An arrow indicates the current frame, which determines the context of most commands. -d(own) [*count*] +.. pdbcommand:: d(own) [count] + Move the current frame *count* (default one) levels down in the stack trace (to a newer frame). -u(p) [*count*] - Move the current frame *count* (default one) levels up in the stack trace - (to an older frame). +.. pdbcommand:: u(p) [count] + + Move the current frame *count* (default one) levels up in the stack trace (to + an older frame). + +.. pdbcommand:: b(reak) [([filename:]lineno | function) [, condition]] -b(reak) [[*filename*:]\ *lineno* | *function*\ [, *condition*]] With a *lineno* argument, set a break there in the current file. With a - *function* argument, set a break at the first executable statement within that - function. The line number may be prefixed with a filename and a colon, to - specify a breakpoint in another file (probably one that hasn't been loaded yet). - The file is searched on ``sys.path``. Note that each breakpoint is assigned a - number to which all the other breakpoint commands refer. - - If a second argument is present, it is an expression which must evaluate to true - before the breakpoint is honored. - - Without argument, list all breaks, including for each breakpoint, the number of - times that breakpoint has been hit, the current ignore count, and the associated - condition if any. - -tbreak [[*filename*:]\ *lineno* | *function*\ [, *condition*]] - Temporary breakpoint, which is removed automatically when it is first hit. The - arguments are the same as break. + *function* argument, set a break at the first executable statement within + that function. The line number may be prefixed with a filename and a colon, + to specify a breakpoint in another file (probably one that hasn't been loaded + yet). The file is searched on :data:`sys.path`. Note that each breakpoint + is assigned a number to which all the other breakpoint commands refer. + + If a second argument is present, it is an expression which must evaluate to + true before the breakpoint is honored. + + Without argument, list all breaks, including for each breakpoint, the number + of times that breakpoint has been hit, the current ignore count, and the + associated condition if any. + +.. pdbcommand:: tbreak [([filename:]lineno | function) [, condition]] + + Temporary breakpoint, which is removed automatically when it is first hit. + The arguments are the same as for :pdbcmd:`break`. + +.. pdbcommand:: cl(ear) [bpnumber [bpnumber ...]] -cl(ear) [*bpnumber* [*bpnumber ...*]] With a space separated list of breakpoint numbers, clear those breakpoints. Without argument, clear all breaks (but first ask confirmation). -disable [*bpnumber* [*bpnumber ...*]] - Disables the breakpoints given as a space separated list of breakpoint numbers. - Disabling a breakpoint means it cannot cause the program to stop execution, but - unlike clearing a breakpoint, it remains in the list of breakpoints and can be - (re-)enabled. - -enable [*bpnumber* [*bpnumber ...*]] - Enables the breakpoints specified. - -ignore *bpnumber* [*count*] - Sets the ignore count for the given breakpoint number. If count is omitted, the - ignore count is set to 0. A breakpoint becomes active when the ignore count is - zero. When non-zero, the count is decremented each time the breakpoint is - reached and the breakpoint is not disabled and any associated condition - evaluates to true. - -condition *bpnumber* [*condition*] - Condition is an expression which must evaluate to true before the breakpoint is - honored. If condition is absent, any existing condition is removed; i.e., the - breakpoint is made unconditional. +.. pdbcommand:: disable [bpnumber [bpnumber ...]] + + Disable the breakpoints given as a space separated list of breakpoint + numbers. Disabling a breakpoint means it cannot cause the program to stop + execution, but unlike clearing a breakpoint, it remains in the list of + breakpoints and can be (re-)enabled. + +.. pdbcommand:: enable [bpnumber [bpnumber ...]] + + Enable the breakpoints specified. + +.. pdbcommand:: ignore bpnumber [count] + + Set the ignore count for the given breakpoint number. If count is omitted, + the ignore count is set to 0. A breakpoint becomes active when the ignore + count is zero. When non-zero, the count is decremented each time the + breakpoint is reached and the breakpoint is not disabled and any associated + condition evaluates to true. + +.. pdbcommand:: condition bpnumber [condition] + + Set a new *condition* for the breakpoint, an expression which must evaluate + to true before the breakpoint is honored. If *condition* is absent, any + existing condition is removed; i.e., the breakpoint is made unconditional. + +.. pdbcommand:: commands [bpnumber] -commands [*bpnumber*] Specify a list of commands for breakpoint number *bpnumber*. The commands - themselves appear on the following lines. Type a line containing just 'end' to - terminate the commands. An example:: + themselves appear on the following lines. Type a line containing just + ``end`` to terminate the commands. An example:: (Pdb) commands 1 (com) print some_variable @@ -273,12 +298,12 @@ (Pdb) To remove all commands from a breakpoint, type commands and follow it - immediately with end; that is, give no commands. + immediately with ``end``; that is, give no commands. With no *bpnumber* argument, commands refers to the last breakpoint set. - You can use breakpoint commands to start your program up again. Simply use the - continue command, or step, or any other command that resumes execution. + You can use breakpoint commands to start your program up again. Simply use + the continue command, or step, or any other command that resumes execution. Specifying any command resuming execution (currently continue, step, next, return, jump, quit and their abbreviations) terminates the command list (as if @@ -292,91 +317,145 @@ that are to print a specific message and then continue. If none of the other commands print anything, you see no sign that the breakpoint was reached. -s(tep) +.. pdbcommand:: s(tep) + Execute the current line, stop at the first possible occasion (either in a function that is called or on the next line in the current function). -n(ext) - Continue execution until the next line in the current function is reached or it - returns. (The difference between ``next`` and ``step`` is that ``step`` stops - inside a called function, while ``next`` executes called functions at (nearly) - full speed, only stopping at the next line in the current function.) - -unt(il) - Continue execution until the line with the line number greater than the - current one is reached or when returning from current frame. +.. pdbcommand:: n(ext) + + Continue execution until the next line in the current function is reached or + it returns. (The difference between :pdbcmd:`next` and :pdbcmd:`step` is + that :pdbcmd:`step` stops inside a called function, while :pdbcmd:`next` + executes called functions at (nearly) full speed, only stopping at the next + line in the current function.) + +.. pdbcommand:: unt(il) [lineno] + + Without argument, continue execution until the line with a number greater + than the current one is reached. + + With a line number, continue execution until a line with a number greater or + equal to that is reached. In both cases, also stop when the current frame + returns. + + .. versionchanged:: 3.2 + Allow giving an explicit line number. + +.. pdbcommand:: r(eturn) -r(eturn) Continue execution until the current function returns. -c(ont(inue)) +.. pdbcommand:: c(ont(inue)) + Continue execution, only stop when a breakpoint is encountered. -j(ump) *lineno* +.. pdbcommand:: j(ump) lineno + Set the next line that will be executed. Only available in the bottom-most - frame. This lets you jump back and execute code again, or jump forward to skip - code that you don't want to run. + frame. This lets you jump back and execute code again, or jump forward to + skip code that you don't want to run. - It should be noted that not all jumps are allowed --- for instance it is not + It should be noted that not all jumps are allowed -- for instance it is not possible to jump into the middle of a :keyword:`for` loop or out of a :keyword:`finally` clause. -l(ist) [*first*\ [, *last*]] - List source code for the current file. Without arguments, list 11 lines around - the current line or continue the previous listing. With one argument, list 11 - lines around at that line. With two arguments, list the given range; if the - second argument is less than the first, it is interpreted as a count. +.. pdbcommand:: l(ist) [first[, last]] + + List source code for the current file. Without arguments, list 11 lines + around the current line or continue the previous listing. With ``.`` as + argument, list 11 lines around the current line. With one argument, + list 11 lines around at that line. With two arguments, list the given range; + if the second argument is less than the first, it is interpreted as a count. + + The current line in the current frame is indicated by ``->``. If an + exception is being debugged, the line where the exception was originally + raised or propagated is indicated by ``>>``, if it differs from the current + line. + + .. versionadded:: 3.2 + The ``>>`` marker. + +.. pdbcommand:: ll | longlist + + List all source code for the current function or frame. Interesting lines + are marked as for :pdbcmd:`list`. + + .. versionadded:: 3.2 + +.. pdbcommand:: a(rgs) -a(rgs) Print the argument list of the current function. -p(rint) *expression* +.. pdbcommand:: p(rint) expression + Evaluate the *expression* in the current context and print its value. -pp *expression* - Like the ``p`` command, except the value of the expression is pretty-printed - using the :mod:`pprint` module. - -alias [*name* [command]] - Creates an alias called *name* that executes *command*. The command must *not* - be enclosed in quotes. Replaceable parameters can be indicated by ``%1``, - ``%2``, and so on, while ``%*`` is replaced by all the parameters. If no - command is given, the current alias for *name* is shown. If no arguments are - given, all aliases are listed. - - Aliases may be nested and can contain anything that can be legally typed at the - pdb prompt. Note that internal pdb commands *can* be overridden by aliases. - Such a command is then hidden until the alias is removed. Aliasing is - recursively applied to the first word of the command line; all other words in - the line are left alone. +.. pdbcommand:: pp expression + + Like the :pdbcmd:`print` command, except the value of the expression is + pretty-printed using the :mod:`pprint` module. + +.. pdbcommand:: whatis expression + + Print the type of the *expression*. + +.. pdbcommand:: source expression + + Try to get source code for the given object and display it. + + .. versionadded:: 3.2 + +.. _debugger-aliases: + +.. pdbcommand:: alias [name [command]] + + Create an alias called *name* that executes *command*. The command must + *not* be enclosed in quotes. Replaceable parameters can be indicated by + ``%1``, ``%2``, and so on, while ``%*`` is replaced by all the parameters. + If no command is given, the current alias for *name* is shown. If no + arguments are given, all aliases are listed. + + Aliases may be nested and can contain anything that can be legally typed at + the pdb prompt. Note that internal pdb commands *can* be overridden by + aliases. Such a command is then hidden until the alias is removed. Aliasing + is recursively applied to the first word of the command line; all other words + in the line are left alone. As an example, here are two useful aliases (especially when placed in the :file:`.pdbrc` file):: - #Print instance variables (usage "pi classInst") + # Print instance variables (usage "pi classInst") alias pi for k in %1.__dict__.keys(): print("%1.",k,"=",%1.__dict__[k]) - #Print instance variables in self + # Print instance variables in self alias ps pi self -unalias *name* - Deletes the specified alias. +.. pdbcommand:: unalias name + + Delete the specified alias. + +.. pdbcommand:: ! statement -[!]\ *statement* Execute the (one-line) *statement* in the context of the current stack frame. The exclamation point can be omitted unless the first word of the statement - resembles a debugger command. To set a global variable, you can prefix the - assignment command with a ``global`` command on the same line, e.g.:: + resembles a debugger command. To set a global variable, you can prefix the + assignment command with a :keyword:`global` statement on the same line, + e.g.:: (Pdb) global list_options; list_options = ['-l'] (Pdb) -run [*args* ...] - Restart the debugged Python program. If an argument is supplied, it is split - with "shlex" and the result is used as the new sys.argv. History, breakpoints, - actions and debugger options are preserved. "restart" is an alias for "run". +.. pdbcommand:: run [args ...] + restart [args ...] + + Restart the debugged Python program. If an argument is supplied, it is split + with :mod:`shlex` and the result is used as the new :data:`sys.argv`. + History, breakpoints, actions and debugger options are preserved. + :pdbcmd:`restart` is an alias for :pdbcmd:`run`. + +.. pdbcommand:: q(uit) -q(uit) - Quit from the debugger. The program being executed is aborted. + Quit from the debugger. The program being executed is aborted. .. rubric:: Footnotes Modified: python/branches/py3k-dtoa/Doc/library/pickletools.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/library/pickletools.rst (original) +++ python/branches/py3k-dtoa/Doc/library/pickletools.rst Sun Aug 8 12:41:24 2010 @@ -13,14 +13,20 @@ :mod:`pickletools` module relevant. -.. function:: dis(pickle, out=None, memo=None, indentlevel=4) +.. function:: dis(pickle, out=None, memo=None, indentlevel=4, annotate=0) - Outputs a symbolic disassembly of the pickle to the file-like object *out*, - defaulting to ``sys.stdout``. *pickle* can be a string or a file-like object. - *memo* can be a Python dictionary that will be used as the pickle's memo; it can - be used to perform disassemblies across multiple pickles created by the same - pickler. Successive levels, indicated by ``MARK`` opcodes in the stream, are - indented by *indentlevel* spaces. + Outputs a symbolic disassembly of the pickle to the file-like + object *out*, defaulting to ``sys.stdout``. *pickle* can be a + string or a file-like object. *memo* can be a Python dictionary + that will be used as the pickle's memo; it can be used to perform + disassemblies across multiple pickles created by the same + pickler. Successive levels, indicated by ``MARK`` opcodes in the + stream, are indented by *indentlevel* spaces. If a nonzero value + is given to *annotate*, each opcode in the output is annotated with + a short description. The value of *annotate* is used as a hint for + the column where annotation should start. + + .. versionadded:: 3.2 the *annotate* argument. .. function:: genops(pickle) Modified: python/branches/py3k-dtoa/Doc/library/pyexpat.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/library/pyexpat.rst (original) +++ python/branches/py3k-dtoa/Doc/library/pyexpat.rst Sun Aug 8 12:41:24 2010 @@ -429,7 +429,7 @@ Called if the XML document hasn't been declared as being a standalone document. This happens when there is an external subset or a reference to a parameter entity, but the XML declaration does not set standalone to ``yes`` in an XML - declaration. If this handler returns ``0``, then the parser will throw an + declaration. If this handler returns ``0``, then the parser will raise an :const:`XML_ERROR_NOT_STANDALONE` error. If this handler is not set, no exception is raised by the parser for this condition. @@ -446,7 +446,7 @@ responsible for creating the sub-parser using ``ExternalEntityParserCreate(context)``, initializing it with the appropriate callbacks, and parsing the entity. This handler should return an integer; if it - returns ``0``, the parser will throw an + returns ``0``, the parser will raise an :const:`XML_ERROR_EXTERNAL_ENTITY_HANDLING` error, otherwise parsing will continue. Modified: python/branches/py3k-dtoa/Doc/library/re.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/library/re.rst (original) +++ python/branches/py3k-dtoa/Doc/library/re.rst Sun Aug 8 12:41:24 2010 @@ -33,8 +33,9 @@ string notation. It is important to note that most regular expression operations are available as -module-level functions and :class:`RegexObject` methods. The functions are -shortcuts that don't require you to compile a regex object first, but miss some +module-level functions and methods on +:ref:`compiled regular expressions `. The functions are shortcuts +that don't require you to compile a regex object first, but miss some fine-tuning parameters. .. seealso:: @@ -485,7 +486,7 @@ Note that for backward compatibility, the :const:`re.U` flag still exists (as well as its synonym :const:`re.UNICODE` and its embedded - counterpart ``(?u)``), but these are redundant in Python 3.0 since + counterpart ``(?u)``), but these are redundant in Python 3 since matches are Unicode by default for strings (and Unicode matching isn't allowed for bytes). @@ -504,7 +505,7 @@ Make ``\w``, ``\W``, ``\b``, ``\B``, ``\s`` and ``\S`` dependent on the current locale. The use of this flag is discouraged as the locale mechanism is very unreliable, and it only handles one "culture" at a time anyway; - you should use Unicode matching instead, which is the default in Python 3.0 + you should use Unicode matching instead, which is the default in Python 3 for Unicode (str) patterns. @@ -546,21 +547,21 @@ -.. function:: search(pattern, string[, flags]) +.. function:: search(pattern, string, flags=0) Scan through *string* looking for a location where the regular expression - *pattern* produces a match, and return a corresponding :class:`MatchObject` - instance. Return ``None`` if no position in the string matches the pattern; note - that this is different from finding a zero-length match at some point in the - string. + *pattern* produces a match, and return a corresponding :ref:`match object + `. Return ``None`` if no position in the string matches the + pattern; note that this is different from finding a zero-length match at some + point in the string. .. function:: match(pattern, string, flags=0) If zero or more characters at the beginning of *string* match the regular - expression *pattern*, return a corresponding :class:`MatchObject` instance. - Return ``None`` if the string does not match the pattern; note that this is - different from a zero-length match. + expression *pattern*, return a corresponding :ref:`match object + `. Return ``None`` if the string does not match the pattern; + note that this is different from a zero-length match. .. note:: @@ -620,9 +621,9 @@ .. function:: finditer(pattern, string, flags=0) - Return an :term:`iterator` yielding :class:`MatchObject` instances over all - non-overlapping matches for the RE *pattern* in *string*. The *string* is - scanned left-to-right, and matches are returned in the order found. Empty + Return an :term:`iterator` yielding :ref:`match objects ` over + all non-overlapping matches for the RE *pattern* in *string*. The *string* + is scanned left-to-right, and matches are returned in the order found. Empty matches are included in the result unless they touch the beginning of another match. @@ -692,6 +693,11 @@ metacharacters in it. +.. function:: purge() + + Clear the regular expression cache. + + .. exception:: error Exception raised when a string passed to one of the functions here is not a @@ -705,107 +711,107 @@ Regular Expression Objects -------------------------- -.. class:: RegexObject - - The :class:`RegexObject` class supports the following methods and attributes: - - .. method:: RegexObject.search(string[, pos[, endpos]]) +Compiled regular expression objects support the following methods and +attributes. - Scan through *string* looking for a location where this regular expression - produces a match, and return a corresponding :class:`MatchObject` instance. - Return ``None`` if no position in the string matches the pattern; note that this - is different from finding a zero-length match at some point in the string. +.. method:: regex.search(string[, pos[, endpos]]) - The optional second parameter *pos* gives an index in the string where the - search is to start; it defaults to ``0``. This is not completely equivalent to - slicing the string; the ``'^'`` pattern character matches at the real beginning - of the string and at positions just after a newline, but not necessarily at the - index where the search is to start. + Scan through *string* looking for a location where this regular expression + produces a match, and return a corresponding :ref:`match object + `. Return ``None`` if no position in the string matches the + pattern; note that this is different from finding a zero-length match at some + point in the string. + + The optional second parameter *pos* gives an index in the string where the + search is to start; it defaults to ``0``. This is not completely equivalent to + slicing the string; the ``'^'`` pattern character matches at the real beginning + of the string and at positions just after a newline, but not necessarily at the + index where the search is to start. + + The optional parameter *endpos* limits how far the string will be searched; it + will be as if the string is *endpos* characters long, so only the characters + from *pos* to ``endpos - 1`` will be searched for a match. If *endpos* is less + than *pos*, no match will be found, otherwise, if *rx* is a compiled regular + expression object, ``rx.search(string, 0, 50)`` is equivalent to + ``rx.search(string[:50], 0)``. - The optional parameter *endpos* limits how far the string will be searched; it - will be as if the string is *endpos* characters long, so only the characters - from *pos* to ``endpos - 1`` will be searched for a match. If *endpos* is less - than *pos*, no match will be found, otherwise, if *rx* is a compiled regular - expression object, ``rx.search(string, 0, 50)`` is equivalent to - ``rx.search(string[:50], 0)``. - - >>> pattern = re.compile("d") - >>> pattern.search("dog") # Match at index 0 - <_sre.SRE_Match object at ...> - >>> pattern.search("dog", 1) # No match; search doesn't include the "d" + >>> pattern = re.compile("d") + >>> pattern.search("dog") # Match at index 0 + <_sre.SRE_Match object at ...> + >>> pattern.search("dog", 1) # No match; search doesn't include the "d" - .. method:: RegexObject.match(string[, pos[, endpos]]) +.. method:: regex.match(string[, pos[, endpos]]) - If zero or more characters at the *beginning* of *string* match this regular - expression, return a corresponding :class:`MatchObject` instance. Return - ``None`` if the string does not match the pattern; note that this is different - from a zero-length match. + If zero or more characters at the *beginning* of *string* match this regular + expression, return a corresponding :ref:`match object `. + Return ``None`` if the string does not match the pattern; note that this is + different from a zero-length match. - The optional *pos* and *endpos* parameters have the same meaning as for the - :meth:`~RegexObject.search` method. + The optional *pos* and *endpos* parameters have the same meaning as for the + :meth:`~regex.search` method. - .. note:: + .. note:: - If you want to locate a match anywhere in *string*, use - :meth:`~RegexObject.search` instead. + If you want to locate a match anywhere in *string*, use + :meth:`~regex.search` instead. - >>> pattern = re.compile("o") - >>> pattern.match("dog") # No match as "o" is not at the start of "dog". - >>> pattern.match("dog", 1) # Match as "o" is the 2nd character of "dog". - <_sre.SRE_Match object at ...> + >>> pattern = re.compile("o") + >>> pattern.match("dog") # No match as "o" is not at the start of "dog". + >>> pattern.match("dog", 1) # Match as "o" is the 2nd character of "dog". + <_sre.SRE_Match object at ...> - .. method:: RegexObject.split(string[, maxsplit=0]) +.. method:: regex.split(string, maxsplit=0) - Identical to the :func:`split` function, using the compiled pattern. + Identical to the :func:`split` function, using the compiled pattern. - .. method:: RegexObject.findall(string[, pos[, endpos]]) +.. method:: regex.findall(string[, pos[, endpos]]) - Similar to the :func:`findall` function, using the compiled pattern, but - also accepts optional *pos* and *endpos* parameters that limit the search - region like for :meth:`match`. + Similar to the :func:`findall` function, using the compiled pattern, but + also accepts optional *pos* and *endpos* parameters that limit the search + region like for :meth:`match`. - .. method:: RegexObject.finditer(string[, pos[, endpos]]) +.. method:: regex.finditer(string[, pos[, endpos]]) - Similar to the :func:`finditer` function, using the compiled pattern, but - also accepts optional *pos* and *endpos* parameters that limit the search - region like for :meth:`match`. + Similar to the :func:`finditer` function, using the compiled pattern, but + also accepts optional *pos* and *endpos* parameters that limit the search + region like for :meth:`match`. - .. method:: RegexObject.sub(repl, string[, count=0]) +.. method:: regex.sub(repl, string, count=0) - Identical to the :func:`sub` function, using the compiled pattern. + Identical to the :func:`sub` function, using the compiled pattern. - .. method:: RegexObject.subn(repl, string[, count=0]) +.. method:: regex.subn(repl, string, count=0) - Identical to the :func:`subn` function, using the compiled pattern. + Identical to the :func:`subn` function, using the compiled pattern. - .. attribute:: RegexObject.flags +.. attribute:: regex.flags - The flags argument used when the RE object was compiled, or ``0`` if no flags - were provided. + The flags argument used when the RE object was compiled, or ``0`` if no flags + were provided. - .. attribute:: RegexObject.groups +.. attribute:: regex.groups - The number of capturing groups in the pattern. + The number of capturing groups in the pattern. - .. attribute:: RegexObject.groupindex +.. attribute:: regex.groupindex - A dictionary mapping any symbolic group names defined by ``(?P)`` to group - numbers. The dictionary is empty if no symbolic groups were used in the - pattern. + A dictionary mapping any symbolic group names defined by ``(?P)`` to group + numbers. The dictionary is empty if no symbolic groups were used in the + pattern. - .. attribute:: RegexObject.pattern +.. attribute:: regex.pattern - The pattern string from which the RE object was compiled. + The pattern string from which the RE object was compiled. .. _match-objects: @@ -813,181 +819,176 @@ Match Objects ------------- -.. class:: MatchObject - - Match Objects always have a boolean value of :const:`True`, so that you can test - whether e.g. :func:`match` resulted in a match with a simple if statement. They - support the following methods and attributes: - - - .. method:: MatchObject.expand(template) +Match objects always have a boolean value of :const:`True`, so that you can test +whether e.g. :func:`match` resulted in a match with a simple if statement. They +support the following methods and attributes: + + +.. method:: match.expand(template) + + Return the string obtained by doing backslash substitution on the template + string *template*, as done by the :meth:`~regex.sub` method. + Escapes such as ``\n`` are converted to the appropriate characters, + and numeric backreferences (``\1``, ``\2``) and named backreferences + (``\g<1>``, ``\g``) are replaced by the contents of the + corresponding group. + + +.. method:: match.group([group1, ...]) + + Returns one or more subgroups of the match. If there is a single argument, the + result is a single string; if there are multiple arguments, the result is a + tuple with one item per argument. Without arguments, *group1* defaults to zero + (the whole match is returned). If a *groupN* argument is zero, the corresponding + return value is the entire matching string; if it is in the inclusive range + [1..99], it is the string matching the corresponding parenthesized group. If a + group number is negative or larger than the number of groups defined in the + pattern, an :exc:`IndexError` exception is raised. If a group is contained in a + part of the pattern that did not match, the corresponding result is ``None``. + If a group is contained in a part of the pattern that matched multiple times, + the last match is returned. + + >>> m = re.match(r"(\w+) (\w+)", "Isaac Newton, physicist") + >>> m.group(0) # The entire match + 'Isaac Newton' + >>> m.group(1) # The first parenthesized subgroup. + 'Isaac' + >>> m.group(2) # The second parenthesized subgroup. + 'Newton' + >>> m.group(1, 2) # Multiple arguments give us a tuple. + ('Isaac', 'Newton') + + If the regular expression uses the ``(?P...)`` syntax, the *groupN* + arguments may also be strings identifying groups by their group name. If a + string argument is not used as a group name in the pattern, an :exc:`IndexError` + exception is raised. + + A moderately complicated example: + + >>> m = re.match(r"(?P\w+) (?P\w+)", "Malcolm Reynolds") + >>> m.group('first_name') + 'Malcolm' + >>> m.group('last_name') + 'Reynolds' + + Named groups can also be referred to by their index: + + >>> m.group(1) + 'Malcolm' + >>> m.group(2) + 'Reynolds' + + If a group matches multiple times, only the last match is accessible: + + >>> m = re.match(r"(..)+", "a1b2c3") # Matches 3 times. + >>> m.group(1) # Returns only the last match. + 'c3' + + +.. method:: match.groups(default=None) + + Return a tuple containing all the subgroups of the match, from 1 up to however + many groups are in the pattern. The *default* argument is used for groups that + did not participate in the match; it defaults to ``None``. - Return the string obtained by doing backslash substitution on the template - string *template*, as done by the :meth:`~RegexObject.sub` method. Escapes - such as ``\n`` are converted to the appropriate characters, and numeric - backreferences (``\1``, ``\2``) and named backreferences (``\g<1>``, - ``\g``) are replaced by the contents of the corresponding group. - - - .. method:: MatchObject.group([group1, ...]) - - Returns one or more subgroups of the match. If there is a single argument, the - result is a single string; if there are multiple arguments, the result is a - tuple with one item per argument. Without arguments, *group1* defaults to zero - (the whole match is returned). If a *groupN* argument is zero, the corresponding - return value is the entire matching string; if it is in the inclusive range - [1..99], it is the string matching the corresponding parenthesized group. If a - group number is negative or larger than the number of groups defined in the - pattern, an :exc:`IndexError` exception is raised. If a group is contained in a - part of the pattern that did not match, the corresponding result is ``None``. - If a group is contained in a part of the pattern that matched multiple times, - the last match is returned. - - >>> m = re.match(r"(\w+) (\w+)", "Isaac Newton, physicist") - >>> m.group(0) # The entire match - 'Isaac Newton' - >>> m.group(1) # The first parenthesized subgroup. - 'Isaac' - >>> m.group(2) # The second parenthesized subgroup. - 'Newton' - >>> m.group(1, 2) # Multiple arguments give us a tuple. - ('Isaac', 'Newton') - - If the regular expression uses the ``(?P...)`` syntax, the *groupN* - arguments may also be strings identifying groups by their group name. If a - string argument is not used as a group name in the pattern, an :exc:`IndexError` - exception is raised. - - A moderately complicated example: - - >>> m = re.match(r"(?P\w+) (?P\w+)", "Malcolm Reynolds") - >>> m.group('first_name') - 'Malcolm' - >>> m.group('last_name') - 'Reynolds' - - Named groups can also be referred to by their index: - - >>> m.group(1) - 'Malcolm' - >>> m.group(2) - 'Reynolds' - - If a group matches multiple times, only the last match is accessible: - - >>> m = re.match(r"(..)+", "a1b2c3") # Matches 3 times. - >>> m.group(1) # Returns only the last match. - 'c3' - - - .. method:: MatchObject.groups(default=None) - - Return a tuple containing all the subgroups of the match, from 1 up to however - many groups are in the pattern. The *default* argument is used for groups that - did not participate in the match; it defaults to ``None``. (Incompatibility - note: in the original Python 1.5 release, if the tuple was one element long, a - string would be returned instead. In later versions (from 1.5.1 on), a - singleton tuple is returned in such cases.) - - For example: + For example: - >>> m = re.match(r"(\d+)\.(\d+)", "24.1632") - >>> m.groups() - ('24', '1632') + >>> m = re.match(r"(\d+)\.(\d+)", "24.1632") + >>> m.groups() + ('24', '1632') - If we make the decimal place and everything after it optional, not all groups - might participate in the match. These groups will default to ``None`` unless - the *default* argument is given: + If we make the decimal place and everything after it optional, not all groups + might participate in the match. These groups will default to ``None`` unless + the *default* argument is given: - >>> m = re.match(r"(\d+)\.?(\d+)?", "24") - >>> m.groups() # Second group defaults to None. - ('24', None) - >>> m.groups('0') # Now, the second group defaults to '0'. - ('24', '0') + >>> m = re.match(r"(\d+)\.?(\d+)?", "24") + >>> m.groups() # Second group defaults to None. + ('24', None) + >>> m.groups('0') # Now, the second group defaults to '0'. + ('24', '0') - .. method:: MatchObject.groupdict([default]) +.. method:: match.groupdict(default=None) - Return a dictionary containing all the *named* subgroups of the match, keyed by - the subgroup name. The *default* argument is used for groups that did not - participate in the match; it defaults to ``None``. For example: + Return a dictionary containing all the *named* subgroups of the match, keyed by + the subgroup name. The *default* argument is used for groups that did not + participate in the match; it defaults to ``None``. For example: - >>> m = re.match(r"(?P\w+) (?P\w+)", "Malcolm Reynolds") - >>> m.groupdict() - {'first_name': 'Malcolm', 'last_name': 'Reynolds'} + >>> m = re.match(r"(?P\w+) (?P\w+)", "Malcolm Reynolds") + >>> m.groupdict() + {'first_name': 'Malcolm', 'last_name': 'Reynolds'} - .. method:: MatchObject.start([group]) - MatchObject.end([group]) +.. method:: match.start([group]) + match.end([group]) - Return the indices of the start and end of the substring matched by *group*; - *group* defaults to zero (meaning the whole matched substring). Return ``-1`` if - *group* exists but did not contribute to the match. For a match object *m*, and - a group *g* that did contribute to the match, the substring matched by group *g* - (equivalent to ``m.group(g)``) is :: + Return the indices of the start and end of the substring matched by *group*; + *group* defaults to zero (meaning the whole matched substring). Return ``-1`` if + *group* exists but did not contribute to the match. For a match object *m*, and + a group *g* that did contribute to the match, the substring matched by group *g* + (equivalent to ``m.group(g)``) is :: - m.string[m.start(g):m.end(g)] + m.string[m.start(g):m.end(g)] - Note that ``m.start(group)`` will equal ``m.end(group)`` if *group* matched a - null string. For example, after ``m = re.search('b(c?)', 'cba')``, - ``m.start(0)`` is 1, ``m.end(0)`` is 2, ``m.start(1)`` and ``m.end(1)`` are both - 2, and ``m.start(2)`` raises an :exc:`IndexError` exception. + Note that ``m.start(group)`` will equal ``m.end(group)`` if *group* matched a + null string. For example, after ``m = re.search('b(c?)', 'cba')``, + ``m.start(0)`` is 1, ``m.end(0)`` is 2, ``m.start(1)`` and ``m.end(1)`` are both + 2, and ``m.start(2)`` raises an :exc:`IndexError` exception. - An example that will remove *remove_this* from email addresses: + An example that will remove *remove_this* from email addresses: - >>> email = "tony at tiremove_thisger.net" - >>> m = re.search("remove_this", email) - >>> email[:m.start()] + email[m.end():] - 'tony at tiger.net' + >>> email = "tony at tiremove_thisger.net" + >>> m = re.search("remove_this", email) + >>> email[:m.start()] + email[m.end():] + 'tony at tiger.net' - .. method:: MatchObject.span([group]) +.. method:: match.span([group]) - For :class:`MatchObject` *m*, return the 2-tuple ``(m.start(group), - m.end(group))``. Note that if *group* did not contribute to the match, this is - ``(-1, -1)``. *group* defaults to zero, the entire match. + For a match *m*, return the 2-tuple ``(m.start(group), m.end(group))``. Note + that if *group* did not contribute to the match, this is ``(-1, -1)``. + *group* defaults to zero, the entire match. - .. attribute:: MatchObject.pos +.. attribute:: match.pos - The value of *pos* which was passed to the :meth:`~RegexObject.search` or - :meth:`~RegexObject.match` method of the :class:`RegexObject`. This is the - index into the string at which the RE engine started looking for a match. + The value of *pos* which was passed to the :meth:`~regex.search` or + :meth:`~regex.match` method of a :ref:`match object `. This + is the index into the string at which the RE engine started looking for a + match. - .. attribute:: MatchObject.endpos +.. attribute:: match.endpos - The value of *endpos* which was passed to the :meth:`~RegexObject.search` or - :meth:`~RegexObject.match` method of the :class:`RegexObject`. This is the - index into the string beyond which the RE engine will not go. + The value of *endpos* which was passed to the :meth:`~regex.search` or + :meth:`~regex.match` method of a :ref:`match object `. This + is the index into the string beyond which the RE engine will not go. - .. attribute:: MatchObject.lastindex +.. attribute:: match.lastindex - The integer index of the last matched capturing group, or ``None`` if no group - was matched at all. For example, the expressions ``(a)b``, ``((a)(b))``, and - ``((ab))`` will have ``lastindex == 1`` if applied to the string ``'ab'``, while - the expression ``(a)(b)`` will have ``lastindex == 2``, if applied to the same - string. + The integer index of the last matched capturing group, or ``None`` if no group + was matched at all. For example, the expressions ``(a)b``, ``((a)(b))``, and + ``((ab))`` will have ``lastindex == 1`` if applied to the string ``'ab'``, while + the expression ``(a)(b)`` will have ``lastindex == 2``, if applied to the same + string. - .. attribute:: MatchObject.lastgroup +.. attribute:: match.lastgroup - The name of the last matched capturing group, or ``None`` if the group didn't - have a name, or if no group was matched at all. + The name of the last matched capturing group, or ``None`` if the group didn't + have a name, or if no group was matched at all. - .. attribute:: MatchObject.re +.. attribute:: match.re - The regular expression object whose :meth:`~RegexObject.match` or - :meth:`~RegexObject.search` method produced this :class:`MatchObject` - instance. + The regular expression object whose :meth:`~regex.match` or + :meth:`~regex.search` method produced this match instance. - .. attribute:: MatchObject.string +.. attribute:: match.string - The string passed to :meth:`~RegexObject.match` or - :meth:`~RegexObject.search`. + The string passed to :meth:`~regex.match` or :meth:`~regex.search`. Examples @@ -1033,8 +1034,7 @@ "" To find out what card the pair consists of, one could use the -:meth:`~MatchObject.group` method of :class:`MatchObject` in the following -manner: +:meth:`~match.group` method of the match object in the following manner: .. doctest:: @@ -1109,7 +1109,7 @@ >>> re.match('Begin (\w| )*? end', s).end() Traceback (most recent call last): File "", line 1, in ? - File "/usr/local/lib/python2.5/re.py", line 132, in match + File "/usr/local/lib/python3.2/re.py", line 132, in match return _compile(pattern, flags).match(string) RuntimeError: maximum recursion limit exceeded @@ -1225,9 +1225,9 @@ ... random.shuffle(inner_word) ... return m.group(1) + "".join(inner_word) + m.group(3) >>> text = "Professor Abdolmalek, please report your absences promptly." - >>> re.sub("(\w)(\w+)(\w)", repl, text) + >>> re.sub(r"(\w)(\w+)(\w)", repl, text) 'Poefsrosr Aealmlobdk, pslaee reorpt your abnseces plmrptoy.' - >>> re.sub("(\w)(\w+)(\w)", repl, text) + >>> re.sub(r"(\w)(\w+)(\w)", repl, text) 'Pofsroser Aodlambelk, plasee reoprt yuor asnebces potlmrpy.' @@ -1248,10 +1248,10 @@ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ If one wants more information about all matches of a pattern than the matched -text, :func:`finditer` is useful as it provides instances of -:class:`MatchObject` instead of strings. Continuing with the previous example, -if one was a writer who wanted to find all of the adverbs *and their positions* -in some text, he or she would use :func:`finditer` in the following manner: +text, :func:`finditer` is useful as it provides :ref:`match objects +` instead of strings. Continuing with the previous example, if +one was a writer who wanted to find all of the adverbs *and their positions* in +some text, he or she would use :func:`finditer` in the following manner: >>> text = "He was carefully disguised but captured quickly by police." >>> for m in re.finditer(r"\w+ly", text): Modified: python/branches/py3k-dtoa/Doc/library/select.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/library/select.rst (original) +++ python/branches/py3k-dtoa/Doc/library/select.rst Sun Aug 8 12:41:24 2010 @@ -41,14 +41,14 @@ .. function:: kqueue() - (Only supported on BSD.) Returns a kernel queue object object; see section + (Only supported on BSD.) Returns a kernel queue object; see section :ref:`kqueue-objects` below for the methods supported by kqueue objects. .. function:: kevent(ident, filter=KQ_FILTER_READ, flags=KQ_EV_ADD, fflags=0, data=0, udata=0) - (Only supported on BSD.) Returns a kernel event object object; see section - :ref:`kevent-objects` below for the methods supported by kqueue objects. + (Only supported on BSD.) Returns a kernel event object; see section + :ref:`kevent-objects` below for the methods supported by kevent objects. .. function:: select(rlist, wlist, xlist[, timeout]) @@ -131,15 +131,15 @@ | :const:`EPOLLONESHOT` | Set one-shot behavior. After one event is | | | pulled out, the fd is internally disabled | +-----------------------+-----------------------------------------------+ - | :const:`EPOLLRDNORM` | ??? | + | :const:`EPOLLRDNORM` | Equivalent to :const:`EPOLLIN` | +-----------------------+-----------------------------------------------+ - | :const:`EPOLLRDBAND` | ??? | + | :const:`EPOLLRDBAND` | Priority data band can be read. | +-----------------------+-----------------------------------------------+ - | :const:`EPOLLWRNORM` | ??? | + | :const:`EPOLLWRNORM` | Equivalent to :const:`EPOLLOUT` | +-----------------------+-----------------------------------------------+ - | :const:`EPOLLWRBAND` | ??? | + | :const:`EPOLLWRBAND` | Priority data may be written. | +-----------------------+-----------------------------------------------+ - | :const:`EPOLLMSG` | ??? | + | :const:`EPOLLMSG` | Ignored. | +-----------------------+-----------------------------------------------+ @@ -233,7 +233,7 @@ .. method:: poll.modify(fd, eventmask) Modifies an already registered fd. This has the same effect as - :meth:`register(fd, eventmask)`. Attempting to modify a file descriptor + ``register(fd, eventmask)``. Attempting to modify a file descriptor that was never registered causes an :exc:`IOError` exception with errno :const:`ENOENT` to be raised. Modified: python/branches/py3k-dtoa/Doc/library/signal.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/library/signal.rst (original) +++ python/branches/py3k-dtoa/Doc/library/signal.rst Sun Aug 8 12:41:24 2010 @@ -76,7 +76,9 @@ .. data:: CTRL_C_EVENT - The signal corresponding to the CTRL+C keystroke event. + The signal corresponding to the CTRL+C keystroke event. This signal can + only be used with :func:`os.kill`. + Availability: Windows. .. versionadded:: 3.2 @@ -84,7 +86,9 @@ .. data:: CTRL_BREAK_EVENT - The signal corresponding to the CTRL+BREAK keystroke event. + The signal corresponding to the CTRL+BREAK keystroke event. This signal can + only be used with :func:`os.kill`. + Availability: Windows. .. versionadded:: 3.2 @@ -226,6 +230,10 @@ see the :ref:`description in the type hierarchy ` or see the attribute descriptions in the :mod:`inspect` module). + On Windows, :func:`signal` can only be called with :const:`SIGABRT`, + :const:`SIGFPE`, :const:`SIGILL`, :const:`SIGINT`, :const:`SIGSEGV`, or + :const:`SIGTERM`. A :exc:`ValueError` will be raised in any other case. + .. _signal-example: Modified: python/branches/py3k-dtoa/Doc/library/smtpd.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/library/smtpd.rst (original) +++ python/branches/py3k-dtoa/Doc/library/smtpd.rst Sun Aug 8 12:41:24 2010 @@ -10,10 +10,14 @@ -This module offers several classes to implement SMTP servers. One is a generic +This module offers several classes to implement SMTP (email) servers. + +Several server implementations are present; one is a generic do-nothing implementation, which can be overridden, while the other two offer specific mail-sending strategies. +Additionally the SMTPChannel may be extended to implement very specific +interaction behaviour with SMTP clients. SMTPServer Objects ------------------ @@ -26,7 +30,6 @@ inherits from :class:`asyncore.dispatcher`, and so will insert itself into :mod:`asyncore`'s event loop on instantiation. - .. method:: process_message(peer, mailfrom, rcpttos, data) Raise :exc:`NotImplementedError` exception. Override this in subclasses to @@ -37,6 +40,11 @@ containing the contents of the e-mail (which should be in :rfc:`2822` format). + .. attribute:: channel_class + + Override this in subclasses to use a custom :class:`SMTPChannel` for + managing SMTP clients. + DebuggingServer Objects ----------------------- @@ -71,3 +79,91 @@ running this has a good chance to make you into an open relay, so please be careful. +SMTPChannel Objects +------------------- + +.. class:: SMTPChannel(server, conn, addr) + + Create a new :class:`SMTPChannel` object which manages the communication + between the server and a single SMTP client. + + To use a custom SMTPChannel implementation you need to override the + :attr:`SMTPServer.channel_class` of your :class:`SMTPServer`. + + The :class:`SMTPChannel` has the following instance variables: + + .. attribute:: smtp_server + + Holds the :class:`SMTPServer` that spawned this channel. + + .. attribute:: conn + + Holds the socket object connecting to the client. + + .. attribute:: addr + + Holds the address of the client, the second value returned by + socket.accept() + + .. attribute:: received_lines + + Holds a list of the line strings (decoded using UTF-8) received from + the client. The lines have their "\r\n" line ending translated to "\n". + + .. attribute:: smtp_state + + Holds the current state of the channel. This will be either + :attr:`COMMAND` initially and then :attr:`DATA` after the client sends + a "DATA" line. + + .. attribute:: seen_greeting + + Holds a string containing the greeting sent by the client in its "HELO". + + .. attribute:: mailfrom + + Holds a string containing the address identified in the "MAIL FROM:" line + from the client. + + .. attribute:: rcpttos + + Holds a list of strings containing the addresses identified in the + "RCPT TO:" lines from the client. + + .. attribute:: received_data + + Holds a string containing all of the data sent by the client during the + DATA state, up to but not including the terminating "\r\n.\r\n". + + .. attribute:: fqdn + + Holds the fully-qualified domain name of the server as returned by + ``socket.getfqdn()``. + + .. attribute:: peer + + Holds the name of the client peer as returned by ``conn.getpeername()`` + where ``conn`` is :attr:`conn`. + + The :class:`SMTPChannel` operates by invoking methods named ``smtp_`` + upon reception of a command line from the client. Built into the base + :class:`SMTPChannel` class are methods for handling the following commands + (and responding to them appropriately): + + ======== =================================================================== + Command Action taken + ======== =================================================================== + HELO Accepts the greeting from the client and stores it in + :attr:`seen_greeting`. + NOOP Takes no action. + QUIT Closes the connection cleanly. + MAIL Accepts the "MAIL FROM:" syntax and stores the supplied address as + :attr:`mailfrom`. + RCPT Accepts the "RCPT TO:" syntax and stores the supplied addresses in + the :attr:`rcpttos` list. + RSET Resets the :attr:`mailfrom`, :attr:`rcpttos`, and + :attr:`received_data`, but not the greeting. + DATA Sets the internal state to :attr:`DATA` and stores remaining lines + from the client in :attr:`received_data` until the terminator + "\r\n.\r\n" is received. + ======== =================================================================== \ No newline at end of file Modified: python/branches/py3k-dtoa/Doc/library/smtplib.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/library/smtplib.rst (original) +++ python/branches/py3k-dtoa/Doc/library/smtplib.rst Sun Aug 8 12:41:24 2010 @@ -284,9 +284,9 @@ and ESMTP options suppressed. This method will return normally if the mail is accepted for at least one - recipient. Otherwise it will throw an exception. That is, if this method does - not throw an exception, then someone should get your mail. If this method does - not throw an exception, it returns a dictionary, with one entry for each + recipient. Otherwise it will raise an exception. That is, if this method does + not raise an exception, then someone should get your mail. If this method does + not raise an exception, it returns a dictionary, with one entry for each recipient that was refused. Each entry contains a tuple of the SMTP error code and the accompanying error message sent by the server. Modified: python/branches/py3k-dtoa/Doc/library/socket.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/library/socket.rst (original) +++ python/branches/py3k-dtoa/Doc/library/socket.rst Sun Aug 8 12:41:24 2010 @@ -748,7 +748,9 @@ Shut down one or both halves of the connection. If *how* is :const:`SHUT_RD`, further receives are disallowed. If *how* is :const:`SHUT_WR`, further sends are disallowed. If *how* is :const:`SHUT_RDWR`, further sends and receives are - disallowed. + disallowed. Depending on the platform, shutting down one half of the connection + can also close the opposite half (e.g. on Mac OS X, ``shutdown(SHUT_WR)`` does + not allow further reads on the other end of the connection). Note that there are no methods :meth:`read` or :meth:`write`; use :meth:`~socket.recv` and :meth:`~socket.send` without *flags* argument instead. Modified: python/branches/py3k-dtoa/Doc/library/sqlite3.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/library/sqlite3.rst (original) +++ python/branches/py3k-dtoa/Doc/library/sqlite3.rst Sun Aug 8 12:41:24 2010 @@ -136,7 +136,7 @@ first blank for the column name: the column name would simply be "x". -.. function:: connect(database[, timeout, isolation_level, detect_types, factory]) +.. function:: connect(database[, timeout, detect_types, isolation_level, check_same_thread, factory, cached_statements]) Opens a connection to the SQLite database file *database*. You can use ``":memory:"`` to open a database connection to a database that resides in RAM @@ -867,3 +867,18 @@ committed: .. literalinclude:: ../includes/sqlite3/ctx_manager.py + + +Common issues +------------- + +Multithreading +^^^^^^^^^^^^^^ + +Older SQLite versions had issues with sharing connections between threads. +That's why the Python module disallows sharing connections and cursors between +threads. If you still try to do so, you will get an exception at runtime. + +The only exception is calling the :meth:`~Connection.interrupt` method, which +only makes sense to call from a different thread. + Modified: python/branches/py3k-dtoa/Doc/library/stdtypes.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/library/stdtypes.rst (original) +++ python/branches/py3k-dtoa/Doc/library/stdtypes.rst Sun Aug 8 12:41:24 2010 @@ -217,14 +217,15 @@ There are three distinct numeric types: :dfn:`integers`, :dfn:`floating point numbers`, and :dfn:`complex numbers`. In addition, Booleans are a subtype of integers. Integers have unlimited precision. Floating point -numbers are implemented using :ctype:`double` in C---all bets on their -precision are off unless you happen to know the machine you are working -with. Complex numbers have a real and imaginary part, which are each -implemented using :ctype:`double` in C. To extract these parts from a -complex number *z*, use ``z.real`` and ``z.imag``. (The standard library -includes additional numeric types, :mod:`fractions` that hold rationals, -and :mod:`decimal` that hold floating-point numbers with user-definable -precision.) +numbers are usually implemented using :ctype:`double` in C; information +about the precision and internal representation of floating point +numbers for the machine on which your program is running is available +in :data:`sys.float_info`. Complex numbers have a real and imaginary +part, which are each a floating point number. To extract these parts +from a complex number *z*, use ``z.real`` and ``z.imag``. (The standard +library includes additional numeric types, :mod:`fractions` that hold +rationals, and :mod:`decimal` that hold floating-point numbers with +user-definable precision.) .. index:: pair: numeric; literals @@ -2264,10 +2265,19 @@ buffer protocol. Builtin objects that support the buffer protocol include :class:`bytes` and :class:`bytearray`. - ``len(view)`` returns the total number of bytes in the memoryview, *view*. + A :class:`memoryview` has the notion of an *element*, which is the + atomic memory unit handled by the originating object *obj*. For many + simple types such as :class:`bytes` and :class:`bytearray`, an element + is a single byte, but other types such as :class:`array.array` may have + bigger elements. + + ``len(view)`` returns the total number of elements in the memoryview, + *view*. The :class:`~memoryview.itemsize` attribute will give you the + number of bytes in a single element. A :class:`memoryview` supports slicing to expose its data. Taking a single - index will return a single byte. Full slicing will result in a subview:: + index will return a single element as a :class:`bytes` object. Full + slicing will result in a subview:: >>> v = memoryview(b'abcefg') >>> v[1] @@ -2278,11 +2288,8 @@ >>> bytes(v[1:4]) b'bce' - >>> v[3:-1] - - >>> bytes(v[4:-1]) - If the object the memory view is over supports changing its data, the + If the object the memoryview is over supports changing its data, the memoryview supports slice assignment:: >>> data = bytearray(b'abcefg') @@ -2302,12 +2309,18 @@ Notice how the size of the memoryview object cannot be changed. - :class:`memoryview` has two methods: .. method:: tobytes() - Return the data in the buffer as a bytestring. + Return the data in the buffer as a bytestring. This is equivalent to + calling the :class:`bytes` constructor on the memoryview. :: + + >>> m = memoryview(b"abc") + >>> m.tobytes() + b'abc' + >>> bytes(m) + b'abc' .. method:: tolist() @@ -2325,7 +2338,15 @@ .. attribute:: itemsize - The size in bytes of each element of the memoryview. + The size in bytes of each element of the memoryview:: + + >>> m = memoryview(array.array('H', [1,2,3])) + >>> m.itemsize + 2 + >>> m[0] + b'\x01\x00' + >>> len(m[0]) == m.itemsize + True .. attribute:: shape Modified: python/branches/py3k-dtoa/Doc/library/string.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/library/string.rst (original) +++ python/branches/py3k-dtoa/Doc/library/string.rst Sun Aug 8 12:41:24 2010 @@ -7,13 +7,13 @@ .. index:: module: re -The :mod:`string` module contains a number of useful constants and classes, as -well as some deprecated legacy functions that are also available as methods on -strings. In addition, Python's built-in string classes support the sequence type -methods described in the :ref:`typesseq` section, and also the string-specific -methods described in the :ref:`string-methods` section. To output formatted -strings, see the :ref:`string-formatting` section. Also, see the :mod:`re` -module for string functions based on regular expressions. +The :mod:`string` module contains a number of useful constants and classes +for string formatting. In addition, Python's built-in string classes +support the sequence type methods described in the :ref:`typesseq` +section, and also the string-specific methods described in the +:ref:`string-methods` section. To output formatted strings, see the +:ref:`string-formatting` section. Also, see the :mod:`re` module for +string functions based on regular expressions. String constants @@ -163,7 +163,7 @@ the format string (integers for positional arguments, and strings for named arguments), and a reference to the *args* and *kwargs* that was passed to vformat. The set of unused args can be calculated from these - parameters. :meth:`check_unused_args` is assumed to throw an exception if + parameters. :meth:`check_unused_args` is assumed to raise an exception if the check fails. .. method:: format_field(value, format_spec) @@ -710,6 +710,14 @@ appropriate). The default value is the regular expression ``[_a-z][_a-z0-9]*``. +* *flags* -- The regular expression flags that will be applied when compiling + the regular expression used for recognizing substitutions. The default value + is ``re.IGNORECASE``. Note that ``re.VERBOSE`` will always be added to the + flags, so custom *idpattern*\ s must follow conventions for verbose regular + expressions. + + .. versionadded:: 3.2 + Alternatively, you can provide the entire regular expression pattern by overriding the class attribute *pattern*. If you do this, the value must be a regular expression object with four named capturing groups. The capturing Modified: python/branches/py3k-dtoa/Doc/library/struct.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/library/struct.rst (original) +++ python/branches/py3k-dtoa/Doc/library/struct.rst Sun Aug 8 12:41:24 2010 @@ -259,18 +259,16 @@ In 3.0, some of the integer formats wrapped out-of-range values and raised :exc:`DeprecationWarning` instead of :exc:`struct.error`. - The ``'p'`` format character encodes a "Pascal string", meaning a short -variable-length string stored in a fixed number of bytes. The count is the total -number of bytes stored. The first byte stored is the length of the string, or -255, whichever is smaller. The bytes of the string follow. If the string -passed in to :func:`pack` is too long (longer than the count minus 1), only the -leading count-1 bytes of the string are stored. If the string is shorter than -count-1, it is padded with null bytes so that exactly count bytes in all are -used. Note that for :func:`unpack`, the ``'p'`` format character consumes count -bytes, but that the string returned can never contain more than 255 bytes. - - +variable-length string stored in a *fixed number of bytes*, given by the count. +The first byte stored is the length of the string, or 255, whichever is +smaller. The bytes of the string follow. If the string passed in to +:func:`pack` is too long (longer than the count minus 1), only the leading +``count-1`` bytes of the string are stored. If the string is shorter than +``count-1``, it is padded with null bytes so that exactly count bytes in all +are used. Note that for :func:`unpack`, the ``'p'`` format character consumes +``count`` bytes, but that the string returned can never contain more than 255 +bytes. For the ``'?'`` format character, the return value is either :const:`True` or :const:`False`. When packing, the truth value of the argument object is used. Modified: python/branches/py3k-dtoa/Doc/library/subprocess.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/library/subprocess.rst (original) +++ python/branches/py3k-dtoa/Doc/library/subprocess.rst Sun Aug 8 12:41:24 2010 @@ -575,7 +575,7 @@ pipe = os.popen(cmd, 'w') ... rc = pipe.close() - if rc is not None and rc % 256: + if rc is not None and rc >> 8: print("There were some errors") ==> process = Popen(cmd, 'w', stdin=PIPE) Modified: python/branches/py3k-dtoa/Doc/library/sys.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/library/sys.rst (original) +++ python/branches/py3k-dtoa/Doc/library/sys.rst Sun Aug 8 12:41:24 2010 @@ -843,7 +843,7 @@ A C function has returned. *arg* is ``None``. ``'c_exception'`` - A C function has thrown an exception. *arg* is ``None``. + A C function has raised an exception. *arg* is ``None``. Note that as an exception is propagated down the chain of callers, an ``'exception'`` event is generated at each level. @@ -935,14 +935,10 @@ .. data:: version A string containing the version number of the Python interpreter plus additional - information on the build number and compiler used. It has a value of the form - ``'version (#build_number, build_date, build_time) [compiler]'``. The first - three characters are used to identify the version in the installation - directories (where appropriate on each platform). An example:: - - >>> import sys - >>> sys.version - '1.5.2 (#0 Apr 13 1999, 10:51:12) [MSC 32 bit (Intel)]' + information on the build number and compiler used. This string is displayed + when the interactive interpreter is started. Do not extract version information + out of it, rather, use :data:`version_info` and the functions provided by the + :mod:`platform` module. .. data:: api_version Modified: python/branches/py3k-dtoa/Doc/library/test.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/library/test.rst (original) +++ python/branches/py3k-dtoa/Doc/library/test.rst Sun Aug 8 12:41:24 2010 @@ -5,6 +5,13 @@ :synopsis: Regression tests package containing the testing suite for Python. .. sectionauthor:: Brett Cannon +.. note:: + The :mod:`test` package is meant for internal use by Python only. It is + documented for the benefit of the core developers of Python. Any use of + this package outside of Python's standard library is discouraged as code + mentioned here can change or be removed without notice between releases of + Python. + The :mod:`test` package contains all regression tests for Python as well as the modules :mod:`test.support` and :mod:`test.regrtest`. Modified: python/branches/py3k-dtoa/Doc/library/threading.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/library/threading.rst (original) +++ python/branches/py3k-dtoa/Doc/library/threading.rst Sun Aug 8 12:41:24 2010 @@ -310,8 +310,8 @@ Return whether the thread is alive. - Roughly, a thread is alive from the moment the :meth:`start` method - returns until its :meth:`run` method terminates. The module function + This method returns ``True`` just before the :meth:`run` method starts + until just after the :meth:`run` method terminates. The module function :func:`.enumerate` returns a list of all alive threads. .. attribute:: daemon Modified: python/branches/py3k-dtoa/Doc/library/tkinter.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/library/tkinter.rst (original) +++ python/branches/py3k-dtoa/Doc/library/tkinter.rst Sun Aug 8 12:41:24 2010 @@ -9,7 +9,9 @@ The :mod:`tkinter` package ("Tk interface") is the standard Python interface to the Tk GUI toolkit. Both Tk and :mod:`tkinter` are available on most Unix platforms, as well as on Windows systems. (Tk itself is not part of Python; it -is maintained at ActiveState.) +is maintained at ActiveState.) You can check that :mod:`tkinter` is properly +installed on your system by running ``python -m tkinter`` from the command line; +this should open a window demonstrating a simple Tk interface. .. seealso:: Modified: python/branches/py3k-dtoa/Doc/library/unittest.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/library/unittest.rst (original) +++ python/branches/py3k-dtoa/Doc/library/unittest.rst Sun Aug 8 12:41:24 2010 @@ -621,20 +621,20 @@ The following decorators implement test skipping and expected failures: -.. function:: skip(reason) +.. decorator:: skip(reason) Unconditionally skip the decorated test. *reason* should describe why the test is being skipped. -.. function:: skipIf(condition, reason) +.. decorator:: skipIf(condition, reason) Skip the decorated test if *condition* is true. -.. function:: skipUnless(condition, reason) +.. decorator:: skipUnless(condition, reason) Skip the decoratored test unless *condition* is true. -.. function:: expectedFailure +.. decorator:: expectedFailure Mark the test as an expected failure. If the test fails when run, the test is not counted as a failure. @@ -1048,11 +1048,11 @@ :attr:`exception` attribute. This can be useful if the intention is to perform additional checks on the exception raised:: - with self.assertRaises(SomeException) as cm: - do_something() + with self.assertRaises(SomeException) as cm: + do_something() - the_exception = cm.exception - self.assertEqual(the_exception.error_code, 3) + the_exception = cm.exception + self.assertEqual(the_exception.error_code, 3) .. versionchanged:: 3.1 Added the ability to use :meth:`assertRaises` as a context manager. Modified: python/branches/py3k-dtoa/Doc/library/urllib.parse.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/library/urllib.parse.rst (original) +++ python/branches/py3k-dtoa/Doc/library/urllib.parse.rst Sun Aug 8 12:41:24 2010 @@ -48,6 +48,23 @@ >>> o.geturl() 'http://www.cwi.nl:80/%7Eguido/Python.html' + If the scheme value is not specified, urlparse following the syntax + specifications from RFC 1808, expects the netloc value to start with '//', + Otherwise, it is not possible to distinguish between net_loc and path + component and would classify the indistinguishable component as path as in + a relative url. + + >>> from urlparse import urlparse + >>> urlparse('//www.cwi.nl:80/%7Eguido/Python.html') + ParseResult(scheme='', netloc='www.cwi.nl:80', path='/%7Eguido/Python.html', + params='', query='', fragment='') + >>> urlparse('www.cwi.nl:80/%7Eguido/Python.html') + ParseResult(scheme='', netloc='', path='www.cwi.nl:80/%7Eguido/Python.html', + params='', query='', fragment='') + >>> urlparse('help/Python.html') + ParseResult(scheme='', netloc='', path='help/Python.html', params='', + query='', fragment='') + If the *scheme* argument is specified, it gives the default addressing scheme, to be used only if the URL does not specify one. The default value for this argument is the empty string. Modified: python/branches/py3k-dtoa/Doc/library/urllib.request.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/library/urllib.request.rst (original) +++ python/branches/py3k-dtoa/Doc/library/urllib.request.rst Sun Aug 8 12:41:24 2010 @@ -638,7 +638,8 @@ :meth:`unknown_open`. Note that the implementation of these methods may involve calls of the parent - :class:`OpenerDirector` instance's :meth:`.open` and :meth:`.error` methods. + :class:`OpenerDirector` instance's :meth:`~OpenerDirector.open` and + :meth:`~OpenerDirector.error` methods. #. Every handler with a method named like :meth:`protocol_response` has that method called to post-process the response. Modified: python/branches/py3k-dtoa/Doc/library/wsgiref.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/library/wsgiref.rst (original) +++ python/branches/py3k-dtoa/Doc/library/wsgiref.rst Sun Aug 8 12:41:24 2010 @@ -187,9 +187,7 @@ .. class:: Headers(headers) Create a mapping-like object wrapping *headers*, which must be a list of header - name/value tuples as described in :pep:`333`. Any changes made to the new - :class:`Headers` object will directly update the *headers* list it was created - with. + name/value tuples as described in :pep:`333`. :class:`Headers` objects support typical mapping operations including :meth:`__getitem__`, :meth:`get`, :meth:`__setitem__`, :meth:`setdefault`, Modified: python/branches/py3k-dtoa/Doc/library/xml.dom.minidom.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/library/xml.dom.minidom.rst (original) +++ python/branches/py3k-dtoa/Doc/library/xml.dom.minidom.rst Sun Aug 8 12:41:24 2010 @@ -136,18 +136,20 @@ .. method:: Node.toxml(encoding=None) - Return the XML that the DOM represents as a string. - - With no argument, the XML header does not specify an encoding, and the result is - Unicode string if the default encoding cannot represent all characters in the - document. Encoding this string in an encoding other than UTF-8 is likely - incorrect, since UTF-8 is the default encoding of XML. - - With an explicit *encoding* [1]_ argument, the result is a byte string in the - specified encoding. It is recommended that this argument is always specified. To - avoid :exc:`UnicodeError` exceptions in case of unrepresentable text data, the - encoding argument should be specified as "utf-8". + Return a string or byte string containing the XML represented by + the DOM node. + With an explicit *encoding* [1]_ argument, the result is a byte + string in the specified encoding. It is recommended that you + always specify an encoding; you may use any encoding you like, but + an argument of "utf-8" is the most common choice, avoiding + :exc:`UnicodeError` exceptions in case of unrepresentable text + data. + + With no *encoding* argument, the result is a Unicode string, and the + XML declaration in the resulting string does not specify an + encoding. Encoding this string in an encoding other than UTF-8 is + likely incorrect, since UTF-8 is the default encoding of XML. .. method:: Node.toprettyxml(indent="", newl="", encoding="") @@ -155,7 +157,8 @@ indentation string and defaults to a tabulator; *newl* specifies the string emitted at the end of each line and defaults to ``\n``. - There's also an *encoding* argument; see :meth:`toxml`. + The *encoding* argument behaves like the corresponding argument of + :meth:`toxml`. .. _dom-example: @@ -240,7 +243,9 @@ .. rubric:: Footnotes -.. [#] The encoding string included in XML output should conform to the - appropriate standards. For example, "UTF-8" is valid, but "UTF8" is - not. See http://www.w3.org/TR/2006/REC-xml11-20060816/#NT-EncodingDecl +.. [#] The encoding name included in the XML output should conform to + the appropriate standards. For example, "UTF-8" is valid, but + "UTF8" is not valid in an XML document's declaration, even though + Python accepts it as an encoding name. + See http://www.w3.org/TR/2006/REC-xml11-20060816/#NT-EncodingDecl and http://www.iana.org/assignments/character-sets . Modified: python/branches/py3k-dtoa/Doc/library/xml.dom.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/library/xml.dom.rst (original) +++ python/branches/py3k-dtoa/Doc/library/xml.dom.rst Sun Aug 8 12:41:24 2010 @@ -976,29 +976,24 @@ Type Mapping ^^^^^^^^^^^^ -The primitive IDL types used in the DOM specification are mapped to Python types +The IDL types used in the DOM specification are mapped to Python types according to the following table. +------------------+-------------------------------------------+ | IDL Type | Python Type | +==================+===========================================+ -| ``boolean`` | ``IntegerType`` (with a value of ``0`` or | -| | ``1``) | +| ``boolean`` | ``bool`` or ``int`` | +------------------+-------------------------------------------+ -| ``int`` | ``IntegerType`` | +| ``int`` | ``int`` | +------------------+-------------------------------------------+ -| ``long int`` | ``IntegerType`` | +| ``long int`` | ``int`` | +------------------+-------------------------------------------+ -| ``unsigned int`` | ``IntegerType`` | +| ``unsigned int`` | ``int`` | ++------------------+-------------------------------------------+ +| ``DOMString`` | ``str`` or ``bytes`` | ++------------------+-------------------------------------------+ +| ``null`` | ``None`` | +------------------+-------------------------------------------+ - -Additionally, the :class:`DOMString` defined in the recommendation is mapped to -a bytes or string object. Applications should be able to handle -Unicode whenever a string is returned from the DOM. - -The IDL ``null`` value is mapped to ``None``, which may be accepted or -provided by the implementation whenever ``null`` is allowed by the API. - .. _dom-accessor-methods: Modified: python/branches/py3k-dtoa/Doc/library/xml.sax.reader.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/library/xml.sax.reader.rst (original) +++ python/branches/py3k-dtoa/Doc/library/xml.sax.reader.rst Sun Aug 8 12:41:24 2010 @@ -154,7 +154,7 @@ Allow an application to set the locale for errors and warnings. SAX parsers are not required to provide localization for errors and warnings; if - they cannot support the requested locale, however, they must throw a SAX + they cannot support the requested locale, however, they must raise a SAX exception. Applications may request a locale change in the middle of a parse. Modified: python/branches/py3k-dtoa/Doc/library/zipfile.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/library/zipfile.rst (original) +++ python/branches/py3k-dtoa/Doc/library/zipfile.rst Sun Aug 8 12:41:24 2010 @@ -12,10 +12,8 @@ defined in `PKZIP Application Note `_. -This module does not currently handle multi-disk ZIP files, or ZIP files -which have appended comments (although it correctly handles comments -added to individual archive members---for which see the :ref:`zipinfo-objects` -documentation). It can handle ZIP files that use the ZIP64 extensions +This module does not currently handle multi-disk ZIP files. +It can handle ZIP files that use the ZIP64 extensions (that is ZIP files that are more than 4 GByte in size). It supports decryption of encrypted files in ZIP archives, but it currently cannot create an encrypted file. Decryption is extremely slow as it is @@ -64,7 +62,6 @@ Returns ``True`` if *filename* is a valid ZIP file based on its magic number, otherwise returns ``False``. *filename* may be a file or file-like object too. - This module does not currently handle ZIP files which have appended comments. .. versionchanged:: 3.1 Support for file and file-like objects. Modified: python/branches/py3k-dtoa/Doc/license.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/license.rst (original) +++ python/branches/py3k-dtoa/Doc/license.rst Sun Aug 8 12:41:24 2010 @@ -106,6 +106,10 @@ +----------------+--------------+------------+------------+-----------------+ | 3.1.1 | 3.1 | 2009 | PSF | yes | +----------------+--------------+------------+------------+-----------------+ +| 3.1.2 | 3.1 | 2010 | PSF | yes | ++----------------+--------------+------------+------------+-----------------+ +| 3.2 | 3.1 | 2010 | PSF | yes | ++----------------+--------------+------------+------------+-----------------+ .. note:: Modified: python/branches/py3k-dtoa/Doc/make.bat ============================================================================== --- python/branches/py3k-dtoa/Doc/make.bat (original) +++ python/branches/py3k-dtoa/Doc/make.bat Sun Aug 8 12:41:24 2010 @@ -34,7 +34,7 @@ goto end :checkout -svn co %SVNROOT%/external/Sphinx-0.6.5/sphinx tools/sphinx +svn co %SVNROOT%/external/Sphinx-1.0.1/sphinx tools/sphinx svn co %SVNROOT%/external/docutils-0.6/docutils tools/docutils svn co %SVNROOT%/external/Jinja-2.3.1/jinja2 tools/jinja2 svn co %SVNROOT%/external/Pygments-1.3.1/pygments tools/pygments Modified: python/branches/py3k-dtoa/Doc/reference/compound_stmts.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/reference/compound_stmts.rst (original) +++ python/branches/py3k-dtoa/Doc/reference/compound_stmts.rst Sun Aug 8 12:41:24 2010 @@ -552,24 +552,27 @@ A class definition defines a class object (see section :ref:`types`): -.. XXX need to document PEP 3115 changes here (new metaclasses) - .. productionlist:: classdef: [`decorators`] "class" `classname` [`inheritance`] ":" `suite` - inheritance: "(" [`expression_list`] ")" + inheritance: "(" [`argument_list` [","] ] ")" classname: `identifier` -A class definition is an executable statement. It first evaluates the -inheritance list, if present. Each item in the inheritance list should evaluate -to a class object or class type which allows subclassing. The class's suite is -then executed in a new execution frame (see section :ref:`naming`), using a -newly created local namespace and the original global namespace. (Usually, the -suite contains only function definitions.) When the class's suite finishes -execution, its execution frame is discarded but its local namespace is -saved. [#]_ A class object is then created using the inheritance list for the -base classes and the saved local namespace for the attribute dictionary. The -class name is bound to this class object in the original local namespace. +A class definition is an executable statement. The inheritance list usually +gives a list of base classes (see :ref:`metaclasses` for more advanced uses), so +each item in the list should evaluate to a class object which allows +subclassing. + +The class's suite is then executed in a new execution frame (see :ref:`naming`), +using a newly created local namespace and the original global namespace. +(Usually, the suite contains mostly function definitions.) When the class's +suite finishes execution, its execution frame is discarded but its local +namespace is saved. [#]_ A class object is then created using the inheritance +list for the base classes and the saved local namespace for the attribute +dictionary. The class name is bound to this class object in the original local +namespace. + +Class creation can be customized heavily using :ref:`metaclasses `. Classes can also be decorated; as with functions, :: @@ -583,25 +586,20 @@ Foo = f1(arg)(f2(Foo)) **Programmer's note:** Variables defined in the class definition are class -variables; they are shared by instances. Instance variables can be set in a -method with ``self.name = value``. Both class and instance variables are -accessible through the notation "``self.name``", and an instance variable hides -a class variable with the same name when accessed in this way. Class variables -can be used as defaults for instance variables, but using mutable values there -can lead to unexpected results. Descriptors can be used to create instance -variables with different implementation details. +attributes; they are shared by instances. Instance attributes can be set in a +method with ``self.name = value``. Both class and instance attributes are +accessible through the notation "``self.name``", and an instance attribute hides +a class attribute with the same name when accessed in this way. Class +attributes can be used as defaults for instance attributes, but using mutable +values there can lead to unexpected results. :ref:`Descriptors ` +can be used to create instance variables with different implementation details. -.. XXX add link to descriptor docs above .. seealso:: + :pep:`3116` - Metaclasses in Python 3 :pep:`3129` - Class Decorators -Class definitions, like function definitions, may be wrapped by one or more -:term:`decorator` expressions. The evaluation rules for the decorator -expressions are the same as for functions. The result must be a class object, -which is then bound to the class name. - .. rubric:: Footnotes Modified: python/branches/py3k-dtoa/Doc/reference/expressions.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/reference/expressions.rst (original) +++ python/branches/py3k-dtoa/Doc/reference/expressions.rst Sun Aug 8 12:41:24 2010 @@ -1031,9 +1031,9 @@ ``x <= y``. If the corresponding element does not exist, the shorter sequence is ordered first (for example, ``[1,2] < [1,2,3]``). -* Mappings (dictionaries) compare equal if and only if their sorted ``(key, - value)`` lists compare equal. [#]_ Outcomes other than equality are resolved - consistently, but are not otherwise defined. [#]_ +* Mappings (dictionaries) compare equal if and only if they have the same + ``(key, value)`` pairs. Order comparisons ``('<', '<=', '>=', '>')`` + raise :exc:`TypeError`. * Sets and frozensets define comparison operators to mean subset and superset tests. Those relations do not define total orderings (the two sets ``{1,2}`` @@ -1330,15 +1330,6 @@ strings in a human recognizable way, compare using :func:`unicodedata.normalize`. -.. [#] The implementation computes this efficiently, without constructing lists - or sorting. - -.. [#] Earlier versions of Python used lexicographic comparison of the sorted (key, - value) lists, but this was very expensive for the common case of comparing - for equality. An even earlier version of Python compared dictionaries by - identity only, but this caused surprises because people expected to be able - to test a dictionary for emptiness by comparing it to ``{}``. - .. [#] Due to automatic garbage-collection, free lists, and the dynamic nature of descriptors, you may notice seemingly unusual behaviour in certain uses of the :keyword:`is` operator, like those involving comparisons between instance Modified: python/branches/py3k-dtoa/Doc/reference/lexical_analysis.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/reference/lexical_analysis.rst (original) +++ python/branches/py3k-dtoa/Doc/reference/lexical_analysis.rst Sun Aug 8 12:41:24 2010 @@ -362,11 +362,12 @@ information on this convention. ``__*__`` - System-defined names. These names are defined by the interpreter and its - implementation (including the standard library); applications should not expect - to define additional names using this convention. The set of names of this - class defined by Python may be extended in future versions. See section - :ref:`specialnames`. + System-defined names. These names are defined by the interpreter and its + implementation (including the standard library). Current system names are + discussed in the :ref:`specialnames` section and elsewhere. More will likely + be defined in future versions of Python. *Any* use of ``__*__`` names, in + any context, that does not follow explicitly documented use, is subject to + breakage without warning. ``__*`` Class-private names. Names in this category, when used within the context of a Modified: python/branches/py3k-dtoa/Doc/tools/sphinx-build.py ============================================================================== --- python/branches/py3k-dtoa/Doc/tools/sphinx-build.py (original) +++ python/branches/py3k-dtoa/Doc/tools/sphinx-build.py Sun Aug 8 12:41:24 2010 @@ -3,11 +3,15 @@ Sphinx - Python documentation toolchain ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - :copyright: 2007 by Georg Brandl. + :copyright: 2007-2010 by Georg Brandl. :license: Python license. """ import sys +import warnings + +# Get rid of UserWarnings reported by pkg_resources. +warnings.filterwarnings('ignore', category=UserWarning, module='jinja2') if __name__ == '__main__': Modified: python/branches/py3k-dtoa/Doc/tools/sphinxext/indexsidebar.html ============================================================================== --- python/branches/py3k-dtoa/Doc/tools/sphinxext/indexsidebar.html (original) +++ python/branches/py3k-dtoa/Doc/tools/sphinxext/indexsidebar.html Sun Aug 8 12:41:24 2010 @@ -2,8 +2,7 @@

Download these documents

Docs for other versions

Modified: python/branches/py3k-dtoa/Doc/tools/sphinxext/pyspecific.py ============================================================================== --- python/branches/py3k-dtoa/Doc/tools/sphinxext/pyspecific.py (original) +++ python/branches/py3k-dtoa/Doc/tools/sphinxext/pyspecific.py Sun Aug 8 12:41:24 2010 @@ -72,6 +72,32 @@ return [pnode] +# Support for documenting decorators + +from sphinx import addnodes +from sphinx.domains.python import PyModulelevel, PyClassmember + +class PyDecoratorMixin(object): + def handle_signature(self, sig, signode): + ret = super(PyDecoratorMixin, self).handle_signature(sig, signode) + signode.insert(0, addnodes.desc_addname('@', '@')) + return ret + + def needs_arglist(self): + return False + +class PyDecoratorFunction(PyDecoratorMixin, PyModulelevel): + def run(self): + # a decorator function is a function after all + self.name = 'py:function' + return PyModulelevel.run(self) + +class PyDecoratorMethod(PyDecoratorMixin, PyClassmember): + def run(self): + self.name = 'py:method' + return PyClassmember.run(self) + + # Support for building "topic help" for pydoc pydoc_topic_labels = [ @@ -119,10 +145,10 @@ for label in self.status_iterator(pydoc_topic_labels, 'building topics... ', length=len(pydoc_topic_labels)): - if label not in self.env.labels: + if label not in self.env.domaindata['std']['labels']: self.warn('label %r not in documentation' % label) continue - docname, labelid, sectname = self.env.labels[label] + docname, labelid, sectname = self.env.domaindata['std']['labels'][label] doctree = self.env.get_and_resolve_doctree(docname, self) document = new_document('
') document.append(doctree.ids[labelid]) @@ -147,7 +173,6 @@ # Support for documenting Opcodes import re -from sphinx import addnodes opcode_sig_re = re.compile(r'(\w+(?:\+\d)?)(?:\s*\((.*)\))?') @@ -165,6 +190,28 @@ return opname.strip() +pdbcmd_sig_re = re.compile(r'([a-z()!]+)\s*(.*)') + +# later... +#pdbargs_tokens_re = re.compile(r'''[a-zA-Z]+ | # identifiers +# [.,:]+ | # punctuation +# [\[\]()] | # parens +# \s+ # whitespace +# ''', re.X) + +def parse_pdb_command(env, sig, signode): + """Transform a pdb command signature into RST nodes.""" + m = pdbcmd_sig_re.match(sig) + if m is None: + raise ValueError + name, args = m.groups() + fullname = name.replace('(', '').replace(')', '') + signode += addnodes.desc_name(name, name) + if args: + signode += addnodes.desc_addname(' '+args, ' '+args) + return fullname + + def setup(app): app.add_role('issue', issue_role) app.add_directive('impl-detail', ImplementationDetail) @@ -172,4 +219,8 @@ app.add_builder(suspicious.CheckSuspiciousMarkupBuilder) app.add_description_unit('opcode', 'opcode', '%s (opcode)', parse_opcode_signature) + app.add_description_unit('pdbcommand', 'pdbcmd', '%s (pdb command)', + parse_pdb_command) app.add_description_unit('2to3fixer', '2to3fixer', '%s (2to3 fixer)') + app.add_directive_to_domain('py', 'decorator', PyDecoratorFunction) + app.add_directive_to_domain('py', 'decoratormethod', PyDecoratorMethod) Modified: python/branches/py3k-dtoa/Doc/tutorial/classes.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/tutorial/classes.rst (original) +++ python/branches/py3k-dtoa/Doc/tutorial/classes.rst Sun Aug 8 12:41:24 2010 @@ -65,7 +65,7 @@ A *namespace* is a mapping from names to objects. Most namespaces are currently implemented as Python dictionaries, but that's normally not noticeable in any way (except for performance), and it may change in the future. Examples of -namespaces are: the set of built-in names (functions such as :func:`abs`, and +namespaces are: the set of built-in names (containing functions such as :func:`abs`, and built-in exception names); the global names in a module; and the local names in a function invocation. In a sense the set of attributes of an object also form a namespace. The important thing to know about namespaces is that there is @@ -599,12 +599,12 @@ possible to access or modify a variable that is considered private. This can even be useful in special circumstances, such as in the debugger. -Notice that code passed to ``exec()``, ``eval()`` or ``execfile()`` does not -consider the classname of the invoking class to be the current class; this is -similar to the effect of the ``global`` statement, the effect of which is -likewise restricted to code that is byte-compiled together. The same -restriction applies to ``getattr()``, ``setattr()`` and ``delattr()``, as well -as when referencing ``__dict__`` directly. +Notice that code passed to ``exec()`` or ``eval()`` does not consider the +classname of the invoking class to be the current class; this is similar to the +effect of the ``global`` statement, the effect of which is likewise restricted +to code that is byte-compiled together. The same restriction applies to +``getattr()``, ``setattr()`` and ``delattr()``, as well as when referencing +``__dict__`` directly. .. _tut-odds: @@ -737,7 +737,7 @@ StopIteration Having seen the mechanics behind the iterator protocol, it is easy to add -iterator behavior to your classes. Define a :meth:`__iter__` method which +iterator behavior to your classes. Define an :meth:`__iter__` method which returns an object with a :meth:`__next__` method. If the class defines :meth:`__next__`, then :meth:`__iter__` can just return ``self``:: @@ -754,7 +754,10 @@ self.index = self.index - 1 return self.data[self.index] - >>> for char in Reverse('spam'): + >>> rev = Reverse('spam') + >>> iter(rev) + <__main__.Reverse object at 0x00A1DB50> + >>> for char in rev: ... print(char) ... m Modified: python/branches/py3k-dtoa/Doc/tutorial/datastructures.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/tutorial/datastructures.rst (original) +++ python/branches/py3k-dtoa/Doc/tutorial/datastructures.rst Sun Aug 8 12:41:24 2010 @@ -377,16 +377,12 @@ Here is a brief demonstration:: - >>> basket = ['apple', 'orange', 'apple', 'pear', 'orange', 'banana'] - >>> fruit = set(basket) # create a set without duplicates - >>> fruit - {'orange', 'pear', 'apple', 'banana'} - >>> fruit = {'orange', 'apple'} # {} syntax is equivalent to [] for lists - >>> fruit - {'orange', 'apple'} - >>> 'orange' in fruit # fast membership testing + >>> basket = {'apple', 'orange', 'apple', 'pear', 'orange', 'banana'} + >>> print(basket) # show that duplicates have been removed + {'orange', 'bananna', 'pear', 'apple'} + >>> 'orange' in basket # fast membership testing True - >>> 'crabgrass' in fruit + >>> 'crabgrass' in basket False >>> # Demonstrate set operations on unique letters from two words Modified: python/branches/py3k-dtoa/Doc/tutorial/floatingpoint.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/tutorial/floatingpoint.rst (original) +++ python/branches/py3k-dtoa/Doc/tutorial/floatingpoint.rst Sun Aug 8 12:41:24 2010 @@ -81,7 +81,7 @@ values share the same approximation, any one of them could be displayed while still preserving the invariant ``eval(repr(x)) == x``. -Historically, the Python prompt and built-in :func:`repr` function would chose +Historically, the Python prompt and built-in :func:`repr` function would choose the one with 17 significant digits, ``0.10000000000000001``. Starting with Python 3.1, Python (on most systems) is now able to choose the shortest of these and simply display ``0.1``. @@ -92,18 +92,17 @@ (although some languages may not *display* the difference by default, or in all output modes). -Python's built-in :func:`str` function produces only 12 significant digits, and -you may wish to use that instead. It's unusual for ``eval(str(x))`` to -reproduce *x*, but the output may be more pleasant to look at:: +For more pleasant output, you may may wish to use string formatting to produce a limited number of significant digits:: - >>> str(math.pi) + >>> format(math.pi, '.12g') # give 12 significant digits '3.14159265359' + >>> format(math.pi, '.2f') # give 2 digits after the point + '3.14' + >>> repr(math.pi) '3.141592653589793' - >>> format(math.pi, '.2f') - '3.14' It's important to realize that this is, in a real sense, an illusion: you're simply rounding the *display* of the true machine value. Modified: python/branches/py3k-dtoa/Doc/using/cmdline.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/using/cmdline.rst (original) +++ python/branches/py3k-dtoa/Doc/using/cmdline.rst Sun Aug 8 12:41:24 2010 @@ -61,7 +61,7 @@ .. cmdoption:: -c - Execute the Python code in *command*. *command* can be one ore more + Execute the Python code in *command*. *command* can be one or more statements separated by newlines, with significant leading whitespace as in normal module code. Modified: python/branches/py3k-dtoa/Doc/whatsnew/2.0.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/whatsnew/2.0.rst (original) +++ python/branches/py3k-dtoa/Doc/whatsnew/2.0.rst Sun Aug 8 12:41:24 2010 @@ -656,7 +656,7 @@ The change which will probably break the most code is tightening up the arguments accepted by some methods. Some methods would take multiple arguments and treat them as a tuple, particularly various list methods such as -:meth:`.append` and :meth:`.insert`. In earlier versions of Python, if ``L`` is +:meth:`append` and :meth:`insert`. In earlier versions of Python, if ``L`` is a list, ``L.append( 1,2 )`` appends the tuple ``(1,2)`` to the list. In Python 2.0 this causes a :exc:`TypeError` exception to be raised, with the message: 'append requires exactly 1 argument; 2 given'. The fix is to simply add an Modified: python/branches/py3k-dtoa/Doc/whatsnew/2.4.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/whatsnew/2.4.rst (original) +++ python/branches/py3k-dtoa/Doc/whatsnew/2.4.rst Sun Aug 8 12:41:24 2010 @@ -1066,7 +1066,7 @@ deprecated APIs and removes support for Python versions earlier than 2.3. The 3.0 version of the package uses a new incremental parser for MIME messages, available in the :mod:`email.FeedParser` module. The new parser doesn't require - reading the entire message into memory, and doesn't throw exceptions if a + reading the entire message into memory, and doesn't raise exceptions if a message is malformed; instead it records any problems in the :attr:`defect` attribute of the message. (Developed by Anthony Baxter, Barry Warsaw, Thomas Wouters, and others.) Modified: python/branches/py3k-dtoa/Doc/whatsnew/2.5.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/whatsnew/2.5.rst (original) +++ python/branches/py3k-dtoa/Doc/whatsnew/2.5.rst Sun Aug 8 12:41:24 2010 @@ -1765,7 +1765,7 @@ http://effbot.org/zone/element-index.htm. ElementTree represents an XML document as a tree of element nodes. The text -content of the document is stored as the :attr:`.text` and :attr:`.tail` +content of the document is stored as the :attr:`text` and :attr:`tail` attributes of (This is one of the major differences between ElementTree and the Document Object Model; in the DOM there are many different types of node, including :class:`TextNode`.) Modified: python/branches/py3k-dtoa/Doc/whatsnew/2.7.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/whatsnew/2.7.rst (original) +++ python/branches/py3k-dtoa/Doc/whatsnew/2.7.rst Sun Aug 8 12:41:24 2010 @@ -146,7 +146,7 @@ * Float-to-string and string-to-float conversions now round their results more correctly, and :func:`repr` of a floating-point number *x* returns a result that's guaranteed to round back to the - same number when converted back to a string. + same number when converted back to a float. * The :ctype:`PyCapsule` type, used to provide a C API for extension modules. * The :cfunc:`PyLong_AsLongAndOverflow` C API function. Modified: python/branches/py3k-dtoa/Doc/whatsnew/3.2.rst ============================================================================== --- python/branches/py3k-dtoa/Doc/whatsnew/3.2.rst (original) +++ python/branches/py3k-dtoa/Doc/whatsnew/3.2.rst Sun Aug 8 12:41:24 2010 @@ -66,18 +66,46 @@ New, Improved, and Deprecated Modules ===================================== -* The :class:`ftplib.FTP` class now supports the context manager protocol - (Contributed by Tarek Ziad?? and Giampaolo Rodol??; :issue:`4972`.) +* The functools module now includes two new decorators for caching function + calls, :func:`functools.lru_cache` and :func:`functools.lfu_cache`. These can + save repeated queries to an external resource whenever the results are + expected to be the same. + + For example, adding a caching decorator to a database query function can save + database accesses for popular searches:: + + @functools.lfu_cache(maxsize=50) + def get_phone_number(name): + c = conn.cursor() + c.execute('SELECT phonenumber FROM phonelist WHERE name=?', (name,)) + return c.fetchone()[0] + + The caches support two strategies for limiting their size to *maxsize*. The + LFU (least-frequently-used) cache works bests when popular queries remain the + same over time. In contrast, the LRU (least-recently-used) cache works best + query popularity changes over time (for example, the most popular news + articles change each day as newer articles are added). + + The two caching decorators can be composed (nested) to handle hybrid cases. + For example, music searches can reflect both long-term patterns (popular + classics) and short-term trends (new releases):: + + @functools.lfu_cache(maxsize=500) + @functools.lru_cache(maxsize=100) + def find_lyrics(song): + query = 'http://www.example.com/songlist/%s' % urllib.quote(song) + page = urllib.urlopen(query).read() + return parse_lyrics(page) + + To help with choosing an effective cache size, the wrapped function + is instrumented with two attributes *hits* and *misses*:: + + >>> for song in user_requests: + ... find_lyrics(song) + >>> print(find_lyrics.hits, find_lyrics.misses) + 4805 980 -* The previously deprecated :func:`string.maketrans` function has been - removed in favor of the static methods, :meth:`bytes.maketrans` and - :meth:`bytearray.maketrans`. This change solves the confusion around which - types were supported by the :mod:`string` module. Now, :class:`str`, - :class:`bytes`, and :class:`bytearray` each have their own **maketrans** and - **translate** methods with intermediate translation tables of the - appropriate type. - - (Contributed by Georg Brandl; :issue:`5675`.) + (Contributed by Raymond Hettinger) * The previously deprecated :func:`contextlib.nested` function has been removed in favor of a plain :keyword:`with` statement which can @@ -88,17 +116,20 @@ (Contributed by Georg Brandl and Mattias Br??ndstr??m; `appspot issue 53094 `_.) +* The :class:`ftplib.FTP` class now supports the context manager protocol + (Contributed by Tarek Ziad?? and Giampaolo Rodol??; :issue:`4972`.) + * The :func:`shutil.copytree` function has two new options: * *ignore_dangling_symlinks*: when ``symlinks=False`` (meaning that the function copies the file pointed to by the symlink, not the symlink - itself) this option will silence the error thrown if the file doesn't + itself) this option will silence the error raised if the file doesn't exist. * *copy_function*: a callable that will be used to copy files. :func:`shutil.copy2` is used by default. - (Contributed by Tarek Ziade.) + (Contributed by Tarek Ziad??.) * The *sqlite3* module has some new features: @@ -111,6 +142,47 @@ are uncommitted changes, and :const:`False` otherwise. (Contributed by R. David Murray and Shashwat Anand, :issue:`8845`.) +* The :mod:`ssl` module has a new class, :class:`~ssl.SSLContext` which + serves as a container for various persistent SSL data, such as protocol + settings, certificates, private keys, and various other options. + The :meth:`~ssl.SSLContext.wrap_socket` method allows to create an + SSL socket from such an SSL context. + (Added by Antoine Pitrou; :issue:`8550`.) + + The :func:`ssl.wrap_socket` constructor function now takes a + *ciphers* argument that's a string listing the encryption algorithms + to be allowed; the format of the string is described + `in the OpenSSL documentation + `__. + (Added by Antoine Pitrou; :issue:`8322`.) + + Various options have been added to the :mod:`ssl` module, such as + :data:`~ssl.OP_NO_SSLv2` which allows to force disabling of the insecure + and obsolete SSLv2 protocol. + (Added by Antoine Pitrou; :issue:`4870`.) + + Another change makes the extension load all of OpenSSL's ciphers and + digest algorithms so that they're all available. Some SSL + certificates couldn't be verified, reporting an "unknown algorithm" + error. (Reported by Beda Kosata, and fixed by Antoine Pitrou; + :issue:`8484`.) + + The version of OpenSSL being used is now available as the module + attributes :data:`ssl.OPENSSL_VERSION` (a string), + :data:`ssl.OPENSSL_VERSION_INFO` (a 5-tuple), and + :data:`ssl.OPENSSL_VERSION_NUMBER` (an integer). (Added by Antoine + Pitrou; :issue:`8321`.) + +* The previously deprecated :func:`string.maketrans` function has been + removed in favor of the static methods, :meth:`bytes.maketrans` and + :meth:`bytearray.maketrans`. This change solves the confusion around which + types were supported by the :mod:`string` module. Now, :class:`str`, + :class:`bytes`, and :class:`bytearray` each have their own **maketrans** and + **translate** methods with intermediate translation tables of the + appropriate type. + + (Contributed by Georg Brandl; :issue:`5675`.) + Multi-threading =============== Modified: python/branches/py3k-dtoa/Grammar/Grammar ============================================================================== --- python/branches/py3k-dtoa/Grammar/Grammar (original) +++ python/branches/py3k-dtoa/Grammar/Grammar Sun Aug 8 12:41:24 2010 @@ -24,13 +24,13 @@ decorated: decorators (classdef | funcdef) funcdef: 'def' NAME parameters ['->' test] ':' suite parameters: '(' [typedargslist] ')' -typedargslist: ((tfpdef ['=' test] ',')* - ('*' [tfpdef] (',' tfpdef ['=' test])* [',' '**' tfpdef] | '**' tfpdef) - | tfpdef ['=' test] (',' tfpdef ['=' test])* [',']) +typedargslist: (tfpdef ['=' test] (',' tfpdef ['=' test])* [',' + ['*' [tfpdef] (',' tfpdef ['=' test])* [',' '**' tfpdef] | '**' tfpdef]] + | '*' [tfpdef] (',' tfpdef ['=' test])* [',' '**' tfpdef] | '**' tfpdef) tfpdef: NAME [':' test] -varargslist: ((vfpdef ['=' test] ',')* - ('*' [vfpdef] (',' vfpdef ['=' test])* [',' '**' vfpdef] | '**' vfpdef) - | vfpdef ['=' test] (',' vfpdef ['=' test])* [',']) +varargslist: (vfpdef ['=' test] (',' vfpdef ['=' test])* [',' + ['*' [vfpdef] (',' vfpdef ['=' test])* [',' '**' vfpdef] | '**' vfpdef]] + | '*' [vfpdef] (',' vfpdef ['=' test])* [',' '**' vfpdef] | '**' vfpdef) vfpdef: NAME stmt: simple_stmt | compound_stmt Modified: python/branches/py3k-dtoa/Include/Python.h ============================================================================== --- python/branches/py3k-dtoa/Include/Python.h (original) +++ python/branches/py3k-dtoa/Include/Python.h Sun Aug 8 12:41:24 2010 @@ -61,6 +61,7 @@ #error "PYMALLOC_DEBUG requires WITH_PYMALLOC" #endif #include "pymath.h" +#include "pytime.h" #include "pymem.h" #include "object.h" @@ -134,13 +135,8 @@ } #endif -/* Convert a possibly signed character to a nonnegative int */ -/* XXX This assumes characters are 8 bits wide */ -#ifdef __CHAR_UNSIGNED__ -#define Py_CHARMASK(c) (c) -#else +/* Argument must be a char or an int in [-128, 127] or [0, 255]. */ #define Py_CHARMASK(c) ((unsigned char)((c) & 0xff)) -#endif #include "pyfpe.h" Modified: python/branches/py3k-dtoa/Include/floatobject.h ============================================================================== --- python/branches/py3k-dtoa/Include/floatobject.h (original) +++ python/branches/py3k-dtoa/Include/floatobject.h Sun Aug 8 12:41:24 2010 @@ -21,12 +21,6 @@ #define PyFloat_Check(op) PyObject_TypeCheck(op, &PyFloat_Type) #define PyFloat_CheckExact(op) (Py_TYPE(op) == &PyFloat_Type) -/* The str() precision PyFloat_STR_PRECISION is chosen so that in most cases, - the rounding noise created by various operations is suppressed, while - giving plenty of precision for practical use. */ - -#define PyFloat_STR_PRECISION 12 - #ifdef Py_NAN #define Py_RETURN_NAN return PyFloat_FromDouble(Py_NAN) #endif Modified: python/branches/py3k-dtoa/Include/patchlevel.h ============================================================================== --- python/branches/py3k-dtoa/Include/patchlevel.h (original) +++ python/branches/py3k-dtoa/Include/patchlevel.h Sun Aug 8 12:41:24 2010 @@ -20,10 +20,10 @@ #define PY_MINOR_VERSION 2 #define PY_MICRO_VERSION 0 #define PY_RELEASE_LEVEL PY_RELEASE_LEVEL_ALPHA -#define PY_RELEASE_SERIAL 0 +#define PY_RELEASE_SERIAL 1 /* Version as a string */ -#define PY_VERSION "3.2a0" +#define PY_VERSION "3.2a1+" /*--end constants--*/ /* Subversion Revision number of this file (not of the repository) */ Modified: python/branches/py3k-dtoa/Include/structseq.h ============================================================================== --- python/branches/py3k-dtoa/Include/structseq.h (original) +++ python/branches/py3k-dtoa/Include/structseq.h Sun Aug 8 12:41:24 2010 @@ -26,17 +26,12 @@ PyAPI_FUNC(PyObject *) PyStructSequence_New(PyTypeObject* type); -typedef struct { - PyObject_VAR_HEAD - PyObject *ob_item[1]; -} PyStructSequence; +typedef PyTupleObject PyStructSequence; /* Macro, *only* to be used to fill in brand new objects */ -#define PyStructSequence_SET_ITEM(op, i, v) \ - (((PyStructSequence *)(op))->ob_item[i] = v) +#define PyStructSequence_SET_ITEM(op, i, v) PyTuple_SET_ITEM(op, i, v) -#define PyStructSequence_GET_ITEM(op, i) \ - (((PyStructSequence *)(op))->ob_item[i]) +#define PyStructSequence_GET_ITEM(op, i) PyTuple_GET_ITEM(op, i) #ifdef __cplusplus Modified: python/branches/py3k-dtoa/LICENSE ============================================================================== --- python/branches/py3k-dtoa/LICENSE (original) +++ python/branches/py3k-dtoa/LICENSE Sun Aug 8 12:41:24 2010 @@ -68,6 +68,7 @@ 3.1 3.0.1 2009 PSF yes 3.1.1 3.1 2009 PSF yes 3.1.2 3.1 2010 PSF yes + 3.2 3.1 2010 PSF yes Footnotes: Modified: python/branches/py3k-dtoa/Lib/_abcoll.py ============================================================================== --- python/branches/py3k-dtoa/Lib/_abcoll.py (original) +++ python/branches/py3k-dtoa/Lib/_abcoll.py Sun Aug 8 12:41:24 2010 @@ -18,11 +18,6 @@ "MappingView", "KeysView", "ItemsView", "ValuesView", "Sequence", "MutableSequence", "ByteString", - "bytearray_iterator", "bytes_iterator", "dict_itemiterator", - "dict_items", "dict_keyiterator", "dict_keys", "dict_proxy", - "dict_valueiterator", "dict_values", "list_iterator", - "list_reverseiterator", "range_iterator", "set_iterator", - "str_iterator", "tuple_iterator", "zip_iterator", ] @@ -480,7 +475,15 @@ except KeyError: pass - def update(self, other=(), **kwds): + def update(*args, **kwds): + if len(args) > 2: + raise TypeError("update() takes at most 2 positional " + "arguments ({} given)".format(len(args))) + elif not args: + raise TypeError("update() takes at least 1 argument (0 given)") + self = args[0] + other = args[1] if len(args) >= 2 else () + if isinstance(other, Mapping): for key in other: self[key] = other[key] Modified: python/branches/py3k-dtoa/Lib/_strptime.py ============================================================================== --- python/branches/py3k-dtoa/Lib/_strptime.py (original) +++ python/branches/py3k-dtoa/Lib/_strptime.py Sun Aug 8 12:41:24 2010 @@ -482,8 +482,8 @@ tt = _strptime(data_string, format)[0] return time.struct_time(tt[:9]) -def _strptime_datetime(class_, data_string, format="%a %b %d %H:%M:%S %Y"): - """Return a class_ instance based on the input string and the +def _strptime_datetime(cls, data_string, format="%a %b %d %H:%M:%S %Y"): + """Return a class cls instance based on the input string and the format string.""" tt, fraction = _strptime(data_string, format) gmtoff, tzname = tt[-2:] @@ -496,4 +496,4 @@ tz = datetime_timezone(tzdelta) args += (tz,) - return class_(*args) + return cls(*args) Modified: python/branches/py3k-dtoa/Lib/argparse.py ============================================================================== --- python/branches/py3k-dtoa/Lib/argparse.py (original) +++ python/branches/py3k-dtoa/Lib/argparse.py Sun Aug 8 12:41:24 2010 @@ -1561,13 +1561,16 @@ # add help and version arguments if necessary # (using explicit default to override global argument_default) + default_prefix = '-' if '-' in prefix_chars else prefix_chars[0] if self.add_help: self.add_argument( - '-h', '--help', action='help', default=SUPPRESS, + default_prefix+'h', default_prefix*2+'help', + action='help', default=SUPPRESS, help=_('show this help message and exit')) if self.version: self.add_argument( - '-v', '--version', action='version', default=SUPPRESS, + default_prefix+'v', default_prefix*2+'version', + action='version', default=SUPPRESS, version=self.version, help=_("show program's version number and exit")) Modified: python/branches/py3k-dtoa/Lib/ast.py ============================================================================== --- python/branches/py3k-dtoa/Lib/ast.py (original) +++ python/branches/py3k-dtoa/Lib/ast.py Sun Aug 8 12:41:24 2010 @@ -50,7 +50,7 @@ if isinstance(node_or_string, Expression): node_or_string = node_or_string.body def _convert(node): - if isinstance(node, Str): + if isinstance(node, (Str, Bytes)): return node.s elif isinstance(node, Num): return node.n @@ -58,6 +58,8 @@ return tuple(map(_convert, node.elts)) elif isinstance(node, List): return list(map(_convert, node.elts)) + elif isinstance(node, Set): + return set(map(_convert, node.elts)) elif isinstance(node, Dict): return dict((_convert(k), _convert(v)) for k, v in zip(node.keys, node.values)) Modified: python/branches/py3k-dtoa/Lib/asyncore.py ============================================================================== --- python/branches/py3k-dtoa/Lib/asyncore.py (original) +++ python/branches/py3k-dtoa/Lib/asyncore.py Sun Aug 8 12:41:24 2010 @@ -435,8 +435,11 @@ self.handle_read() def handle_connect_event(self): - self.connected = True + err = self.socket.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR) + if err != 0: + raise socket.error(err, _strerror(err)) self.handle_connect() + self.connected = True def handle_write_event(self): if self.accepting: @@ -607,6 +610,14 @@ def send(self, *args): return os.write(self.fd, *args) + def getsockopt(self, level, optname, buflen=None): + if (level == socket.SOL_SOCKET and + optname == socket.SO_ERROR and + not buflen): + return 0 + raise NotImplementedError("Only asyncore specific behaviour " + "implemented.") + read = recv write = send Modified: python/branches/py3k-dtoa/Lib/base64.py ============================================================================== --- python/branches/py3k-dtoa/Lib/base64.py (original) +++ python/branches/py3k-dtoa/Lib/base64.py Sun Aug 8 12:41:24 2010 @@ -241,7 +241,7 @@ acc += _b32rev[c] << shift shift -= 5 if shift < 0: - parts.append(binascii.unhexlify('%010x' % acc)) + parts.append(binascii.unhexlify(bytes('%010x' % acc, "ascii"))) acc = 0 shift = 35 # Process the last, partial quanta Modified: python/branches/py3k-dtoa/Lib/bdb.py ============================================================================== --- python/branches/py3k-dtoa/Lib/bdb.py (original) +++ python/branches/py3k-dtoa/Lib/bdb.py Sun Aug 8 12:41:24 2010 @@ -109,6 +109,8 @@ self.is_skipped_module(frame.f_globals.get('__name__')): return False if frame is self.stopframe: + if self.stoplineno == -1: + return False return frame.f_lineno >= self.stoplineno while frame is not None and frame is not self.stopframe: if frame is self.botframe: @@ -165,23 +167,28 @@ but only if we are to stop at or just below this level.""" pass - def _set_stopinfo(self, stopframe, returnframe, stoplineno=-1): + def _set_stopinfo(self, stopframe, returnframe, stoplineno=0): self.stopframe = stopframe self.returnframe = returnframe self.quitting = 0 + # stoplineno >= 0 means: stop at line >= the stoplineno + # stoplineno -1 means: don't stop at all self.stoplineno = stoplineno # Derived classes and clients can call the following methods # to affect the stepping state. - def set_until(self, frame): #the name "until" is borrowed from gdb + def set_until(self, frame, lineno=None): """Stop when the line with the line no greater than the current one is reached or when returning from current frame""" - self._set_stopinfo(frame, frame, frame.f_lineno+1) + # the name "until" is borrowed from gdb + if lineno is None: + lineno = frame.f_lineno + 1 + self._set_stopinfo(frame, frame, lineno) def set_step(self): """Stop after one line of code.""" - self._set_stopinfo(None,None) + self._set_stopinfo(None, None) def set_next(self, frame): """Stop on the next line in or below the given frame.""" @@ -208,7 +215,7 @@ def set_continue(self): # Don't stop except at breakpoints or when finished - self._set_stopinfo(self.botframe, None) + self._set_stopinfo(self.botframe, None, -1) if not self.breaks: # no breakpoints; run without debugger overhead sys.settrace(None) @@ -263,15 +270,9 @@ def clear_bpbynumber(self, arg): try: - number = int(arg) - except: - return 'Non-numeric breakpoint number (%s)' % arg - try: - bp = Breakpoint.bpbynumber[number] - except IndexError: - return 'Breakpoint number (%d) out of range' % number - if not bp: - return 'Breakpoint (%d) already deleted' % number + bp = self.get_bpbynumber(arg) + except ValueError as err: + return str(err) self.clear_break(bp.file, bp.line) def clear_all_file_breaks(self, filename): @@ -292,6 +293,21 @@ bp.deleteMe() self.breaks = {} + def get_bpbynumber(self, arg): + if not arg: + raise ValueError('Breakpoint number expected') + try: + number = int(arg) + except ValueError: + raise ValueError('Non-numeric breakpoint number %s' % arg) + try: + bp = Breakpoint.bpbynumber[number] + except IndexError: + raise ValueError('Breakpoint number %d out of range' % number) + if bp is None: + raise ValueError('Breakpoint %d already deleted' % number) + return bp + def get_break(self, filename, lineno): filename = self.canonic(filename) return filename in self.breaks and \ @@ -361,8 +377,9 @@ if line: s = s + lprefix + line.strip() return s - # The following two methods can be called by clients to use - # a debugger to debug a statement, given as a string. + # The following methods can be called by clients to use + # a debugger to debug a statement or an expression. + # Both can be given as a string, or a code object. def run(self, cmd, globals=None, locals=None): if globals is None: @@ -372,8 +389,6 @@ locals = globals self.reset() sys.settrace(self.trace_dispatch) - if not isinstance(cmd, types.CodeType): - cmd = cmd+'\n' try: exec(cmd, globals, locals) except BdbQuit: @@ -390,8 +405,6 @@ locals = globals self.reset() sys.settrace(self.trace_dispatch) - if not isinstance(expr, types.CodeType): - expr = expr+'\n' try: return eval(expr, globals, locals) except BdbQuit: @@ -486,6 +499,9 @@ def bpprint(self, out=None): if out is None: out = sys.stdout + print(self.bpformat(), file=out) + + def bpformat(self): if self.temporary: disp = 'del ' else: @@ -494,17 +510,22 @@ disp = disp + 'yes ' else: disp = disp + 'no ' - print('%-4dbreakpoint %s at %s:%d' % (self.number, disp, - self.file, self.line), file=out) + ret = '%-4dbreakpoint %s at %s:%d' % (self.number, disp, + self.file, self.line) if self.cond: - print('\tstop only if %s' % (self.cond,), file=out) + ret += '\n\tstop only if %s' % (self.cond,) if self.ignore: - print('\tignore next %d hits' % (self.ignore), file=out) - if (self.hits): - if (self.hits > 1): ss = 's' - else: ss = '' - print(('\tbreakpoint already hit %d time%s' % - (self.hits, ss)), file=out) + ret += '\n\tignore next %d hits' % (self.ignore,) + if self.hits: + if self.hits > 1: + ss = 's' + else: + ss = '' + ret += '\n\tbreakpoint already hit %d time%s' % (self.hits, ss) + return ret + + def __str__(self): + return 'breakpoint %s at %s:%s' % (self.number, self.file, self.line) # -----------end of Breakpoint class---------- Modified: python/branches/py3k-dtoa/Lib/cProfile.py ============================================================================== --- python/branches/py3k-dtoa/Lib/cProfile.py (original) +++ python/branches/py3k-dtoa/Lib/cProfile.py Sun Aug 8 12:41:24 2010 @@ -36,7 +36,7 @@ result = prof.print_stats(sort) return result -def runctx(statement, globals, locals, filename=None): +def runctx(statement, globals, locals, filename=None, sort=-1): """Run statement under profiler, supplying your own globals and locals, optionally saving results in filename. @@ -53,7 +53,7 @@ if filename is not None: prof.dump_stats(filename) else: - result = prof.print_stats() + result = prof.print_stats(sort) return result # ____________________________________________________________ @@ -164,7 +164,8 @@ parser.add_option('-o', '--outfile', dest="outfile", help="Save stats to ", default=None) parser.add_option('-s', '--sort', dest="sort", - help="Sort order when printing to stdout, based on pstats.Stats class", default=-1) + help="Sort order when printing to stdout, based on pstats.Stats class", + default=-1) if not sys.argv[1:]: parser.print_usage() @@ -173,14 +174,18 @@ (options, args) = parser.parse_args() sys.argv[:] = args - if (len(sys.argv) > 0): - sys.path.insert(0, os.path.dirname(sys.argv[0])) - fp = open(sys.argv[0]) - try: - script = fp.read() - finally: - fp.close() - run('exec(%r)' % script, options.outfile, options.sort) + if len(args) > 0: + progname = args[0] + sys.path.insert(0, os.path.dirname(progname)) + with open(progname, 'rb') as fp: + code = compile(fp.read(), progname, 'exec') + globs = { + '__file__': progname, + '__name__': '__main__', + '__package__': None, + '__cached__': None, + } + runctx(code, globs, None, options.outfile, options.sort) else: parser.print_usage() return parser Modified: python/branches/py3k-dtoa/Lib/cmd.py ============================================================================== --- python/branches/py3k-dtoa/Lib/cmd.py (original) +++ python/branches/py3k-dtoa/Lib/cmd.py Sun Aug 8 12:41:24 2010 @@ -84,7 +84,6 @@ sys.stdin and sys.stdout are used. """ - import sys if stdin is not None: self.stdin = stdin else: @@ -134,7 +133,7 @@ if not len(line): line = 'EOF' else: - line = line[:-1] # chop \n + line = line.rstrip('\r\n') line = self.precmd(line) stop = self.onecmd(line) stop = self.postcmd(stop, line) Modified: python/branches/py3k-dtoa/Lib/collections.py ============================================================================== --- python/branches/py3k-dtoa/Lib/collections.py (original) +++ python/branches/py3k-dtoa/Lib/collections.py Sun Aug 8 12:41:24 2010 @@ -11,16 +11,12 @@ from keyword import iskeyword as _iskeyword import sys as _sys import heapq as _heapq -from weakref import proxy as _proxy from itertools import repeat as _repeat, chain as _chain, starmap as _starmap ################################################################################ ### OrderedDict ################################################################################ -class _Link(object): - __slots__ = 'prev', 'next', 'key', '__weakref__' - class OrderedDict(dict, MutableMapping): 'Dictionary that remembers insertion order' # An inherited dict maps keys to values. @@ -31,9 +27,7 @@ # The internal self.__map dictionary maps keys to links in a doubly linked list. # The circular doubly linked list starts and ends with a sentinel element. # The sentinel element never gets deleted (this simplifies the algorithm). - # The prev/next links are weakref proxies (to prevent circular references). - # Individual links are kept alive by the hard reference in self.__map. - # Those hard references disappear when a key is deleted from an OrderedDict. + # Each link is stored as a list of length three: [PREV, NEXT, KEY]. def __init__(self, *args, **kwds): '''Initialize an ordered dictionary. Signature is the same as for @@ -46,56 +40,51 @@ try: self.__root except AttributeError: - self.__root = root = _Link() # sentinel node for the doubly linked list - root.prev = root.next = root + self.__root = root = [None, None, None] # sentinel node + PREV = 0 + NEXT = 1 + root[PREV] = root[NEXT] = root self.__map = {} self.update(*args, **kwds) - def clear(self): - 'od.clear() -> None. Remove all items from od.' - root = self.__root - root.prev = root.next = root - self.__map.clear() - dict.clear(self) - - def __setitem__(self, key, value): + def __setitem__(self, key, value, PREV=0, NEXT=1, dict_setitem=dict.__setitem__): 'od.__setitem__(i, y) <==> od[i]=y' # Setting a new item creates a new link which goes at the end of the linked # list, and the inherited dictionary is updated with the new key/value pair. if key not in self: - self.__map[key] = link = _Link() root = self.__root - last = root.prev - link.prev, link.next, link.key = last, root, key - last.next = root.prev = _proxy(link) - dict.__setitem__(self, key, value) + last = root[PREV] + last[NEXT] = root[PREV] = self.__map[key] = [last, root, key] + dict_setitem(self, key, value) - def __delitem__(self, key): + def __delitem__(self, key, PREV=0, NEXT=1, dict_delitem=dict.__delitem__): 'od.__delitem__(y) <==> del od[y]' # Deleting an existing item uses self.__map to find the link which is # then removed by updating the links in the predecessor and successor nodes. - dict.__delitem__(self, key) + dict_delitem(self, key) link = self.__map.pop(key) - link.prev.next = link.next - link.next.prev = link.prev + link_prev = link[PREV] + link_next = link[NEXT] + link_prev[NEXT] = link_next + link_next[PREV] = link_prev - def __iter__(self): + def __iter__(self, NEXT=1, KEY=2): 'od.__iter__() <==> iter(od)' # Traverse the linked list in order. root = self.__root - curr = root.next + curr = root[NEXT] while curr is not root: - yield curr.key - curr = curr.next + yield curr[KEY] + curr = curr[NEXT] - def __reversed__(self): + def __reversed__(self, PREV=0, KEY=2): 'od.__reversed__() <==> reversed(od)' # Traverse the linked list in reverse order. root = self.__root - curr = root.prev + curr = root[PREV] while curr is not root: - yield curr.key - curr = curr.prev + yield curr[KEY] + curr = curr[PREV] def __reduce__(self): 'Return state information for pickling' @@ -108,12 +97,24 @@ return (self.__class__, (items,), inst_dict) return self.__class__, (items,) + def clear(self): + 'od.clear() -> None. Remove all items from od.' + try: + for node in self.__map.values(): + del node[:] + self.__root[:] = [self.__root, self.__root, None] + self.__map.clear() + except AttributeError: + pass + dict.clear(self) + setdefault = MutableMapping.setdefault update = MutableMapping.update pop = MutableMapping.pop keys = MutableMapping.keys values = MutableMapping.values items = MutableMapping.items + __ne__ = MutableMapping.__ne__ def popitem(self, last=True): '''od.popitem() -> (k, v), return and remove a (key, value) pair. @@ -157,13 +158,8 @@ all(p==q for p, q in zip(self.items(), other.items())) return dict.__eq__(self, other) - def __ne__(self, other): - '''od.__ne__(y) <==> od!=y. Comparison to another OD is order-sensitive - while comparison to a regular mapping is order-insensitive. - - ''' - return not self == other - + def __del__(self): + self.clear() # eliminate cyclical references ################################################################################ @@ -244,7 +240,7 @@ return result \n def __repr__(self): 'Return a nicely formatted representation string' - return '%(typename)s(%(reprtxt)s)' %% self \n + return self.__class__.__name__ + '(%(reprtxt)s)' %% self \n def _asdict(self): 'Return a new OrderedDict which maps field names to their values' return OrderedDict(zip(self._fields, self)) \n Modified: python/branches/py3k-dtoa/Lib/configparser.py ============================================================================== --- python/branches/py3k-dtoa/Lib/configparser.py (original) +++ python/branches/py3k-dtoa/Lib/configparser.py Sun Aug 8 12:41:24 2010 @@ -1,6 +1,6 @@ """Configuration file parser. -A setup file consists of sections, lead by a "[section]" header, +A configuration file consists of sections, lead by a "[section]" header, and followed by "name: value" entries, with continuations and such in the style of RFC 822. @@ -24,67 +24,88 @@ methods: - __init__(defaults=None) - create the parser and specify a dictionary of intrinsic defaults. The - keys must be strings, the values must be appropriate for %()s string - interpolation. Note that `__name__' is always an intrinsic default; - its value is the section's name. + __init__(defaults=None, dict_type=_default_dict, + delimiters=('=', ':'), comment_prefixes=('#', ';'), + empty_lines_in_values=True, allow_no_value=False): + Create the parser. When `defaults' is given, it is initialized into the + dictionary or intrinsic defaults. The keys must be strings, the values + must be appropriate for %()s string interpolation. Note that `__name__' + is always an intrinsic default; its value is the section's name. + + When `dict_type' is given, it will be used to create the dictionary + objects for the list of sections, for the options within a section, and + for the default values. + + When `delimiters' is given, it will be used as the set of substrings + that divide keys from values. + + When `comment_prefixes' is given, it will be used as the set of + substrings that prefix comments in a line. + + When `empty_lines_in_values' is False (default: True), each empty line + marks the end of an option. Otherwise, internal empty lines of + a multiline option are kept as part of the value. + + When `allow_no_value' is True (default: False), options without + values are accepted; the value presented for these is None. sections() - return all the configuration section names, sans DEFAULT + Return all the configuration section names, sans DEFAULT. has_section(section) - return whether the given section exists + Return whether the given section exists. has_option(section, option) - return whether the given option exists in the given section + Return whether the given option exists in the given section. options(section) - return list of configuration options for the named section + Return list of configuration options for the named section. - read(filenames) - read and parse the list of named configuration files, given by + read(filenames, encoding=None) + Read and parse the list of named configuration files, given by name. A single filename is also allowed. Non-existing files are ignored. Return list of successfully read files. readfp(fp, filename=None) - read and parse one configuration file, given as a file object. + Read and parse one configuration file, given as a file object. The filename defaults to fp.name; it is only used in error messages (if fp has no `name' attribute, the string `' is used). get(section, option, raw=False, vars=None) - return a string value for the named option. All % interpolations are + Return a string value for the named option. All % interpolations are expanded in the return values, based on the defaults passed into the constructor and the DEFAULT section. Additional substitutions may be provided using the `vars' argument, which must be a dictionary whose contents override any pre-existing defaults. getint(section, options) - like get(), but convert value to an integer + Like get(), but convert value to an integer. getfloat(section, options) - like get(), but convert value to a float + Like get(), but convert value to a float. getboolean(section, options) - like get(), but convert value to a boolean (currently case + Like get(), but convert value to a boolean (currently case insensitively defined as 0, false, no, off for False, and 1, true, yes, on for True). Returns False or True. items(section, raw=False, vars=None) - return a list of tuples with (name, value) for each option + Return a list of tuples with (name, value) for each option in the section. remove_section(section) - remove the given file section and all its options + Remove the given file section and all its options. remove_option(section, option) - remove the given option from the given section + Remove the given option from the given section. set(section, option, value) - set the given option + Set the given option. - write(fp) - write the configuration state in .ini format + write(fp, space_around_delimiters=True) + Write the configuration state in .ini format. If + `space_around_delimiters' is True (the default), delimiters + between keys and values are surrounded by spaces. """ try: @@ -94,6 +115,7 @@ _default_dict = dict import re +import sys __all__ = ["NoSectionError", "DuplicateSectionError", "NoOptionError", "InterpolationError", "InterpolationDepthError", @@ -114,17 +136,19 @@ def _get_message(self): """Getter for 'message'; needed only to override deprecation in - BaseException.""" + BaseException. + """ return self.__message def _set_message(self, value): """Setter for 'message'; needed only to override deprecation in - BaseException.""" + BaseException. + """ self.__message = value # BaseException.message has been deprecated since Python 2.6. To prevent - # DeprecationWarning from popping up over this pre-existing attribute, use - # a new property that takes lookup precedence. + # DeprecationWarning from popping up over this pre-existing attribute, use a + # new property that takes lookup precedence. message = property(_get_message, _set_message) def __init__(self, msg=''): @@ -136,12 +160,15 @@ __str__ = __repr__ + class NoSectionError(Error): """Raised when no section matches a requested option.""" def __init__(self, section): Error.__init__(self, 'No section: %r' % (section,)) self.section = section + self.args = (section, ) + class DuplicateSectionError(Error): """Raised when a section is multiply-created.""" @@ -149,6 +176,8 @@ def __init__(self, section): Error.__init__(self, "Section %r already exists" % section) self.section = section + self.args = (section, ) + class NoOptionError(Error): """A requested option was not found.""" @@ -158,6 +187,8 @@ (option, section)) self.option = option self.section = section + self.args = (option, section) + class InterpolationError(Error): """Base class for interpolation-related exceptions.""" @@ -166,6 +197,8 @@ Error.__init__(self, msg) self.option = option self.section = section + self.args = (option, section, msg) + class InterpolationMissingOptionError(InterpolationError): """A string substitution required a setting which was not available.""" @@ -179,11 +212,14 @@ % (section, option, reference, rawval)) InterpolationError.__init__(self, option, section, msg) self.reference = reference + self.args = (option, section, rawval, reference) + class InterpolationSyntaxError(InterpolationError): """Raised when the source text into which substitutions are made does not conform to the required syntax.""" + class InterpolationDepthError(InterpolationError): """Raised when substitutions are nested too deeply.""" @@ -194,6 +230,8 @@ "\trawval : %s\n" % (section, option, rawval)) InterpolationError.__init__(self, option, section, msg) + self.args = (option, section, rawval) + class ParsingError(Error): """Raised when a configuration file does not follow legal syntax.""" @@ -202,11 +240,13 @@ Error.__init__(self, 'File contains parsing errors: %s' % filename) self.filename = filename self.errors = [] + self.args = (filename, ) def append(self, lineno, line): self.errors.append((lineno, line)) self.message += '\n\t[line %2d]: %s' % (lineno, line) + class MissingSectionHeaderError(ParsingError): """Raised when a key-value pair is found before any section header.""" @@ -218,21 +258,76 @@ self.filename = filename self.lineno = lineno self.line = line + self.args = (filename, lineno, line) class RawConfigParser: + """ConfigParser that does not do interpolation.""" + + # Regular expressions for parsing section headers and options + _SECT_TMPL = r""" + \[ # [ + (?P
[^]]+) # very permissive! + \] # ] + """ + _OPT_TMPL = r""" + (?P