diff -r 27b5bd5f0e4c -r b17f9a10235f Doc/library/stdtypes.rst --- a/Doc/library/stdtypes.rst Sat Sep 01 15:00:34 2012 +0200 +++ b/Doc/library/stdtypes.rst Sat Sep 01 16:38:00 2012 +0400 @@ -2376,8 +2376,8 @@ >>> data bytearray(b'z1spam') - Memoryviews of hashable (read-only) types with formats 'B', 'b' or 'c' - are also hashable. The hash is defined as ``hash(m) == hash(m.tobytes())``:: + Memoryviews of hashable (read-only) types are also hashable. The hash + is defined as ``hash(m) == hash(m.tobytes())``:: >>> v = memoryview(b'abcefg') >>> hash(v) == hash(b'abcefg') @@ -2387,13 +2387,21 @@ >>> hash(v[::-2]) == hash(b'abcefg'[::-2]) True + Hashing of multi-dimensional objects is supported:: + + >>> buf = bytes(list(range(12))) + >>> x = memoryview(buf) + >>> y = x.cast('B', shape=[2,2,3]) + >>> x.tolist() + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] + >>> y.tolist() + [[[0, 1, 2], [3, 4, 5]], [[6, 7, 8], [9, 10, 11]]] + >>> hash(x) == hash(y) == hash(y.tobytes()) + True + .. versionchanged:: 3.3 - Memoryview objects with formats 'B', 'b' or 'c' are now hashable. - - .. note:: - Hashing of memoryviews with formats other than 'B', 'b' or 'c' is - possible in version 3.3.0, but will raise an error in 3.3.1 in order - to be compatible with the new memoryview equality definition. + Memoryview objects are now hashable. + :class:`memoryview` has several methods: diff -r 27b5bd5f0e4c -r b17f9a10235f Doc/library/zipfile.rst --- a/Doc/library/zipfile.rst Sat Sep 01 15:00:34 2012 +0200 +++ b/Doc/library/zipfile.rst Sat Sep 01 16:38:00 2012 +0400 @@ -97,6 +97,30 @@ .. versionadded:: 3.3 +.. data:: PERMS_PRESERVE_NONE + + Constant for use in :meth:`extractall` and :meth:`extract` methods. Do not + preserve permissions of zipped files. + + .. versionadded:: 3.3 + +.. data:: PERMS_PRESERVE_SAFE + + Constant for use in :meth:`extractall` and :meth:`extract` methods. Preserve + safe subset of permissions of the zipped files only: permissions for reading, + writing, execution for user, group and others. + + .. versionadded:: 3.3 + +.. data:: PERMS_PRESERVE_ALL + + Constant for use in :meth:`extractall` and :meth:`extract` methods. Preserve + all the permissions of the zipped files, including unsafe ones: UID bit + (:data:`stat.S_ISUID`), group UID bit (:data:`stat.S_ISGID`), sticky bit + (:data:`stat.S_ISVTX`). + + .. versionadded:: 3.3 + .. data:: ZIP_LZMA The numeric constant for the LZMA compression method. This requires the @@ -234,21 +258,27 @@ ZIP file that contains members with duplicate names. -.. method:: ZipFile.extract(member, path=None, pwd=None) +.. method:: ZipFile.extract(member, path=None, pwd=None, preserve_permissions=zipfile.PERMS_PRESERVE_NONE) Extract a member from the archive to the current working directory; *member* must be its full name or a :class:`ZipInfo` object). Its file information is extracted as accurately as possible. *path* specifies a different directory to extract to. *member* can be a filename or a :class:`ZipInfo` object. - *pwd* is the password used for encrypted files. + *pwd* is the password used for encrypted files. *preserve_permissions* controls whether permissions + of zipped files are preserved or not. Default is :data:`PERMS_PRESERVE_NONE` – + do not preserve any permissions. Other options are to preserve safe subset of + permissions (:data:`PERMS_PRESERVE_SAFE`) or all permissions (:data:`PERMS_PRESERVE_ALL`). -.. method:: ZipFile.extractall(path=None, members=None, pwd=None) +.. method:: ZipFile.extractall(path=None, members=None, pwd=None, preserve_permissions=zipfile.PERMS_PRESERVE_NONE) Extract all members from the archive to the current working directory. *path* specifies a different directory to extract to. *members* is optional and must be a subset of the list returned by :meth:`namelist`. *pwd* is the password - used for encrypted files. + used for encrypted files. *preserve_permissions* controls whether permissions + of zipped files are preserved or not. Default is :data:`PERMS_PRESERVE_NONE` – + do not preserve any permissions. Other options are to preserve safe subset of + permissions (:data:`PERMS_PRESERVE_SAFE`) or all permissions (:data:`PERMS_PRESERVE_ALL`). .. warning:: diff -r 27b5bd5f0e4c -r b17f9a10235f Doc/whatsnew/3.3.rst --- a/Doc/whatsnew/3.3.rst Sat Sep 01 15:00:34 2012 +0200 +++ b/Doc/whatsnew/3.3.rst Sat Sep 01 16:38:00 2012 +0400 @@ -1119,7 +1119,7 @@ +---------+-------------+--------------+-------------+ | | decimal.py | _decimal | speedup | +=========+=============+==============+=============+ - | pi | 42.02s | 0.345s | 120x | + | pi | 38.89s | 0.38s | 100x | +---------+-------------+--------------+-------------+ | telco | 172.19s | 5.68s | 30x | +---------+-------------+--------------+-------------+ diff -r 27b5bd5f0e4c -r b17f9a10235f Include/pyfpe.h --- a/Include/pyfpe.h Sat Sep 01 15:00:34 2012 +0200 +++ b/Include/pyfpe.h Sat Sep 01 16:38:00 2012 +0400 @@ -4,8 +4,8 @@ extern "C" { #endif /* - --------------------------------------------------------------------- - / Copyright (c) 1996. \ + --------------------------------------------------------------------- + / Copyright (c) 1996. \ | The Regents of the University of California. | | All rights reserved. | | | @@ -37,8 +37,8 @@ | opinions of authors expressed herein do not necessarily state or | | reflect those of the United States Government or the University | | of California, and shall not be used for advertising or product | - \ endorsement purposes. / - --------------------------------------------------------------------- + \ endorsement purposes. / + --------------------------------------------------------------------- */ /* diff -r 27b5bd5f0e4c -r b17f9a10235f Lib/argparse.py --- a/Lib/argparse.py Sat Sep 01 15:00:34 2012 +0200 +++ b/Lib/argparse.py Sat Sep 01 16:38:00 2012 +0400 @@ -1722,7 +1722,10 @@ if action.dest is not SUPPRESS: if not hasattr(namespace, action.dest): if action.default is not SUPPRESS: - setattr(namespace, action.dest, action.default) + default = action.default + if isinstance(action.default, str): + default = self._get_value(action, default) + setattr(namespace, action.dest, default) # add any parser defaults that aren't present for dest in self._defaults: @@ -1945,24 +1948,9 @@ # if we didn't consume all the argument strings, there were extras extras.extend(arg_strings[stop_index:]) - # make sure all required actions were present and also convert - # action defaults which were not given as arguments - required_actions = [] - for action in self._actions: - if action not in seen_actions: - if action.required: - required_actions.append(_get_action_name(action)) - else: - # Convert action default now instead of doing it before - # parsing arguments to avoid calling convert functions - # twice (which may fail) if the argument was given, but - # only if it was defined already in the namespace - if (action.default is not None and - hasattr(namespace, action.dest) and - action.default is getattr(namespace, action.dest)): - setattr(namespace, action.dest, - self._get_value(action, action.default)) - + # make sure all required actions were present + required_actions = [_get_action_name(action) for action in self._actions + if action.required and action not in seen_actions] if required_actions: self.error(_('the following arguments are required: %s') % ', '.join(required_actions)) diff -r 27b5bd5f0e4c -r b17f9a10235f Lib/test/test_argparse.py --- a/Lib/test/test_argparse.py Sat Sep 01 15:00:34 2012 +0200 +++ b/Lib/test/test_argparse.py Sat Sep 01 16:38:00 2012 +0400 @@ -1463,22 +1463,6 @@ ('readonly', NS(x=None, spam=RFile('readonly'))), ] -class TestFileTypeDefaults(TempDirMixin, ParserTestCase): - """Test that a file is not created unless the default is needed""" - def setUp(self): - super(TestFileTypeDefaults, self).setUp() - file = open(os.path.join(self.temp_dir, 'good'), 'w') - file.write('good') - file.close() - - argument_signatures = [ - Sig('-c', type=argparse.FileType('r'), default='no-file.txt'), - ] - # should provoke no such file error - failures = [''] - # should not provoke error because default file is created - successes = [('-c good', NS(c=RFile('good')))] - class TestFileTypeRB(TempDirMixin, ParserTestCase): """Test the FileType option/argument type for reading files""" @@ -4575,38 +4559,6 @@ self.assertNotIn(msg, 'optional_positional') -# ================================================ -# Check that the type function is called only once -# ================================================ - -class TestTypeFunctionCallOnlyOnce(TestCase): - - def test_type_function_call_only_once(self): - def spam(string_to_convert): - self.assertEqual(string_to_convert, 'spam!') - return 'foo_converted' - - parser = argparse.ArgumentParser() - parser.add_argument('--foo', type=spam, default='bar') - args = parser.parse_args('--foo spam!'.split()) - self.assertEqual(NS(foo='foo_converted'), args) - -# ================================================================ -# Check that the type function is called with a non-string default -# ================================================================ - -class TestTypeFunctionCallWithNonStringDefault(TestCase): - - def test_type_function_call_with_non_string_default(self): - def spam(int_to_convert): - self.assertEqual(int_to_convert, 0) - return 'foo_converted' - - parser = argparse.ArgumentParser() - parser.add_argument('--foo', type=spam, default=0) - args = parser.parse_args([]) - self.assertEqual(NS(foo='foo_converted'), args) - # ====================== # parse_known_args tests # ====================== diff -r 27b5bd5f0e4c -r b17f9a10235f Lib/test/test_mailbox.py --- a/Lib/test/test_mailbox.py Sat Sep 01 15:00:34 2012 +0200 +++ b/Lib/test/test_mailbox.py Sat Sep 01 16:38:00 2012 +0400 @@ -763,13 +763,13 @@ self.assertIsNot(match, None, "Invalid file name: '%s'" % tail) groups = match.groups() if previous_groups is not None: - self.assertGreaterEqual(int(groups[0]), int(previous_groups[0]), + self.assertTrue(int(groups[0] >= previous_groups[0]), "Non-monotonic seconds: '%s' before '%s'" % (previous_groups[0], groups[0])) - if int(groups[0]) == int(previous_groups[0]): - self.assertGreaterEqual(int(groups[1]), int(previous_groups[1]), - "Non-monotonic milliseconds: '%s' before '%s'" % - (previous_groups[1], groups[1])) + self.assertTrue(int(groups[1] >= previous_groups[1]) or + groups[0] != groups[1], + "Non-monotonic milliseconds: '%s' before '%s'" % + (previous_groups[1], groups[1])) self.assertEqual(int(groups[2]), pid, "Process ID mismatch: '%s' should be '%s'" % (groups[2], pid)) diff -r 27b5bd5f0e4c -r b17f9a10235f Lib/test/test_zipfile.py --- a/Lib/test/test_zipfile.py Sat Sep 01 15:00:34 2012 +0200 +++ b/Lib/test/test_zipfile.py Sat Sep 01 16:38:00 2012 +0400 @@ -4,6 +4,7 @@ import imp import time import shutil +import stat import struct import zipfile import unittest @@ -636,6 +637,102 @@ unlink(TESTFN2) +class TestsPermissionExtraction(unittest.TestCase): + def setUp(self): + permissions = { + 'user': (stat.S_IRUSR, stat.S_IWUSR, stat.S_IXUSR), + 'group': (stat.S_IRGRP, stat.S_IWGRP, stat.S_IXGRP), + 'other': (stat.S_IROTH, stat.S_IWOTH, stat.S_IXOTH), + 'special': (stat.S_ISUID, stat.S_ISGID, stat.S_ISVTX) + } + self.files = [] + os.mkdir(TESTFNDIR) + for permgroup in ('user', 'group', 'other'): + for index in range(8): + for specialindex in range(3): + filename = '{dir:s}/{permgroup:s}_{octalcode:03b}_{specialcode:03b}'.\ + format(dir=TESTFNDIR, permgroup=permgroup, octalcode=index, specialcode=specialindex) + with open(filename, 'wt') as file_: + file_.write(filename) + mode = stat.S_IRUSR + for order in range(3): + if index & 1 << order: + mode |= permissions[permgroup][order] + for order in range(3): + if specialindex & 1 << order: + mode |= permissions['special'][order] + os.chmod(filename, mode) + real_permission = os.stat(filename).st_mode & 0xFFF + self.files.append((filename, real_permission)) + with zipfile.ZipFile(TESTFN2, 'w', zipfile.ZIP_STORED) as zipfp: + for filename, mode in self.files: + zipfp.write(filename) + os.remove(filename) + os.rmdir(TESTFNDIR) + + def test_extractall_preserve_none(self): + umask = os.umask(0) + os.umask(umask) + with zipfile.ZipFile(TESTFN2, 'r') as zipfp: + zipfp.extractall() + for filename, mode in self.files: + expected_mode = 0o666 & ~umask + self.assertTrue(os.path.exists(filename)) + self.assertEqual(os.stat(filename).st_mode & 0xFFF, expected_mode) + shutil.rmtree(TESTFNDIR) + + def test_extractall_preserve_safe(self): + with zipfile.ZipFile(TESTFN2, 'r') as zipfp: + zipfp.extractall(preserve_permissions = zipfile.PERMS_PRESERVE_SAFE) + for filename, mode in self.files: + expected_mode = mode & 0x1FF + self.assertTrue(os.path.exists(filename)) + self.assertEqual(os.stat(filename).st_mode & 0xFFF, expected_mode) + shutil.rmtree(TESTFNDIR) + + def test_extractall_preserve_all(self): + with zipfile.ZipFile(TESTFN2, 'r') as zipfp: + zipfp.extractall(preserve_permissions = zipfile.PERMS_PRESERVE_ALL) + for filename, mode in self.files: + self.assertTrue(os.path.exists(filename)) + self.assertEqual(os.stat(filename).st_mode & 0xFFF, mode) + shutil.rmtree(TESTFNDIR) + + def test_extract_preserve_none(self): + umask = os.umask(0) + os.umask(umask) + with zipfile.ZipFile(TESTFN2, 'r') as zipfp: + for filename, mode in self.files: + zipfp.extract(filename) + expected_mode = 0o666 & ~umask + self.assertTrue(os.path.exists(filename)) + self.assertEqual(os.stat(filename).st_mode & 0xFFF, expected_mode) + shutil.rmtree(TESTFNDIR) + + def test_extract_preserve_safe(self): + with zipfile.ZipFile(TESTFN2, 'r') as zipfp: + for filename, mode in self.files: + zipfp.extract(filename, preserve_permissions = zipfile.PERMS_PRESERVE_SAFE) + expected_mode = mode & 0x1FF + self.assertTrue(os.path.exists(filename)) + self.assertEqual(os.stat(filename).st_mode & 0xFFF, expected_mode) + shutil.rmtree(TESTFNDIR) + + def test_extract_preserve_all(self): + with zipfile.ZipFile(TESTFN2, 'r') as zipfp: + for filename, mode in self.files: + zipfp.extract(filename, preserve_permissions = zipfile.PERMS_PRESERVE_ALL) + self.assertTrue(os.path.exists(filename)) + self.assertEqual(os.stat(filename).st_mode & 0xFFF, mode) + shutil.rmtree(TESTFNDIR) + + def tearDown(self): + if os.path.exists(TESTFNDIR): + shutil.rmtree(TESTFNDIR) + if os.path.exists(TESTFN2): + os.remove(TESTFN2) + + class TestZip64InSmallFiles(unittest.TestCase): # These tests test the ZIP64 functionality without using large files, # see test_zipfile64 for proper tests. @@ -1780,7 +1877,7 @@ run_unittest(TestsWithSourceFile, TestZip64InSmallFiles, OtherTests, PyZipFileTests, DecryptionTests, TestsWithMultipleOpens, TestWithDirectory, UniversalNewlineTests, - TestsWithRandomBinaryFiles) + TestsWithRandomBinaryFiles, TestsPermissionExtraction) if __name__ == "__main__": test_main() diff -r 27b5bd5f0e4c -r b17f9a10235f Lib/zipfile.py --- a/Lib/zipfile.py Sat Sep 01 15:00:34 2012 +0200 +++ b/Lib/zipfile.py Sat Sep 01 16:38:00 2012 +0400 @@ -34,6 +34,7 @@ __all__ = ["BadZipFile", "BadZipfile", "error", "ZIP_STORED", "ZIP_DEFLATED", "ZIP_BZIP2", "ZIP_LZMA", + "PERMS_PRESERVE_NONE", 'PERMS_PRESERVE_SAFE', "PERMS_PRESERVE_ALL", "is_zipfile", "ZipInfo", "ZipFile", "PyZipFile", "LargeZipFile"] class BadZipFile(Exception): @@ -60,6 +61,9 @@ ZIP_LZMA = 14 # Other ZIP compression methods not supported +#Enum choices for Zipfile.extractall preserve_permissions argument +PERMS_PRESERVE_NONE, PERMS_PRESERVE_SAFE, PERMS_PRESERVE_ALL = range(3) + DEFAULT_VERSION = 20 ZIP64_VERSION = 45 BZIP2_VERSION = 46 @@ -160,6 +164,7 @@ _CD64_DIRECTORY_SIZE = 8 _CD64_OFFSET_START_CENTDIR = 9 + def _check_zipfile(fp): try: if _EndRecData(fp): @@ -1192,7 +1197,7 @@ return ZipExtFile(zef_file, mode, zinfo, zd, close_fileobj=not self._filePassed) - def extract(self, member, path=None, pwd=None): + def extract(self, member, path=None, pwd=None, preserve_permissions=PERMS_PRESERVE_NONE): """Extract a member from the archive to the current working directory, using its full name. Its file information is extracted as accurately as possible. `member' may be a filename or a ZipInfo object. You can @@ -1204,21 +1209,24 @@ if path is None: path = os.getcwd() - return self._extract_member(member, path, pwd) + return self._extract_member(member, path, pwd, preserve_permissions) - def extractall(self, path=None, members=None, pwd=None): + def extractall(self, path=None, members=None, pwd=None, preserve_permissions=PERMS_PRESERVE_NONE): """Extract all members from the archive to the current working directory. `path' specifies a different directory to extract to. `members' is optional and must be a subset of the list returned - by namelist(). + by namelist(). `preserve_permissions` controls whether permissions + of zipped files are preserved or not. Default is PERMS_PRESERVE_NONE - + do not preserve any permissions. Other options are to preserve safe subset of + permissions PERMS_PRESERVE_SAFE or all permissions PERMS_PRESERVE_ALL. """ if members is None: members = self.namelist() for zipinfo in members: - self.extract(zipinfo, path, pwd) + self.extract(zipinfo, path, pwd, preserve_permissions) - def _extract_member(self, member, targetpath, pwd): + def _extract_member(self, member, targetpath, pwd, preserve_permissions): """Extract the ZipInfo object 'member' to a physical file on the path targetpath. """ @@ -1253,6 +1261,16 @@ source.close() target.close() + if preserve_permissions in (PERMS_PRESERVE_SAFE, PERMS_PRESERVE_ALL): + if preserve_permissions == PERMS_PRESERVE_ALL: + #preserve bits 0-11: sugrwxrwxrwx, this include + #sticky bit, uid bit, gid bit + mode = member.external_attr >> 16 & 0xFFF + elif PERMS_PRESERVE_SAFE: + #preserve bits 0-8 only: rwxrwxrwx + mode = member.external_attr >> 16 & 0x1FF + os.chmod(targetpath, mode) + return targetpath def _writecheck(self, zinfo): diff -r 27b5bd5f0e4c -r b17f9a10235f Misc/ACKS --- a/Misc/ACKS Sat Sep 01 15:00:34 2012 +0200 +++ b/Misc/ACKS Sat Sep 01 16:38:00 2012 +0400 @@ -114,6 +114,7 @@ Forest Bond Gregory Bond Matias Bordese +Alexey Boriskin Jurjen Bos Peter Bosch Dan Boswell @@ -335,7 +336,6 @@ Frederik Fix Matt Fleming Hernán Martínez Foffani -Arnaud Fontaine Michael Foord Amaury Forgeot d'Arc Doug Fort diff -r 27b5bd5f0e4c -r b17f9a10235f Misc/NEWS --- a/Misc/NEWS Sat Sep 01 15:00:34 2012 +0200 +++ b/Misc/NEWS Sat Sep 01 16:38:00 2012 +0400 @@ -16,21 +16,12 @@ Library ------- -- Issue #12776,#11839: call argparse type function (specified by add_argument) - only once. Before, the type function was called twice in the case where the - default was specified and the argument was given as well. This was - especially problematic for the FileType type, as a default file would always - be opened, even if a file argument was specified on the command line. - Extension Modules ----------------- Tests ----- -- Issue #15802: Fix test logic in TestMaildir.test_create_tmp. Patch - by Serhiy Storchaka. - Build ----- @@ -73,13 +64,6 @@ always returning an integer. So it is now possible to distinguish special keys like keypad keys. -- Issue #14223: Fix window.addch() of the curses module for special characters - like curses.ACS_HLINE: the Python function addch(int) and addch(bytes) is now - calling the C function waddch()/mvwaddch() (as it was done in Python 3.2), - instead of wadd_wch()/mvwadd_wch(). The Python function addch(str) is still - calling the C function wadd_wch()/mvwadd_wch() if the Python curses is linked - to libncursesw. - What's New in Python 3.3.0 Release Candidate 1? =============================================== diff -r 27b5bd5f0e4c -r b17f9a10235f Modules/_cursesmodule.c --- a/Modules/_cursesmodule.c Sat Sep 01 15:00:34 2012 +0200 +++ b/Modules/_cursesmodule.c Sat Sep 01 16:38:00 2012 +0400 @@ -280,6 +280,7 @@ #endif ) { + int ret = 0; long value; #ifdef HAVE_NCURSESW wchar_t buffer[2]; @@ -303,6 +304,7 @@ } else if(PyBytes_Check(obj) && PyBytes_Size(obj) == 1) { value = (unsigned char)PyBytes_AsString(obj)[0]; + ret = 1; } else if (PyLong_CheckExact(obj)) { int overflow; @@ -312,6 +314,11 @@ "int doesn't fit in long"); return 0; } +#ifdef HAVE_NCURSESW + ret = 2; +#else + ret = 1; +#endif } else { PyErr_Format(PyExc_TypeError, @@ -319,14 +326,27 @@ Py_TYPE(obj)->tp_name); return 0; } - - *ch = (chtype)value; - if ((long)*ch != value) { - PyErr_Format(PyExc_OverflowError, - "byte doesn't fit in chtype"); - return 0; +#ifdef HAVE_NCURSESW + if (ret == 2) { + memset(wch->chars, 0, sizeof(wch->chars)); + wch->chars[0] = (wchar_t)value; + if ((long)wch->chars[0] != value) { + PyErr_Format(PyExc_OverflowError, + "character doesn't fit in wchar_t"); + return 0; + } } - return 1; + else +#endif + { + *ch = (chtype)value; + if ((long)*ch != value) { + PyErr_Format(PyExc_OverflowError, + "byte doesn't fit in chtype"); + return 0; + } + } + return ret; } /* Convert an object to a byte string (char*) or a wide character string diff -r 27b5bd5f0e4c -r b17f9a10235f setup.py --- a/setup.py Sat Sep 01 15:00:34 2012 +0200 +++ b/setup.py Sat Sep 01 16:38:00 2012 +0400 @@ -1897,7 +1897,7 @@ def _decimal_ext(self): extra_compile_args = [] - undef_macros = [] + undef_macros = ['NDEBUG'] if '--with-system-libmpdec' in sysconfig.get_config_var("CONFIG_ARGS"): include_dirs = [] libraries = ['mpdec']