[Python-checkins] cpython (merge default -> default): merge

christian.heimes python-checkins at python.org
Sat Nov 23 15:59:20 CET 2013


http://hg.python.org/cpython/rev/16dd19aa64c8
changeset:   87423:16dd19aa64c8
parent:      87422:63df21e74c65
parent:      87420:b5e9d61f6987
user:        Christian Heimes <christian at cheimes.de>
date:        Sat Nov 23 15:59:07 2013 +0100
summary:
  merge

files:
  Doc/library/zipfile.rst                       |  17 +-
  Lib/pathlib.py                                |   4 +-
  Lib/test/test_pathlib.py                      |  14 +-
  Lib/test/test_zipfile.py                      |   4 +-
  Lib/test/test_zipfile64.py                    |   4 +-
  Lib/tkinter/test/test_tkinter/test_widgets.py |   3 +
  Lib/tkinter/test/widget_tests.py              |   4 +
  Lib/unittest/loader.py                        |  60 ++++++-
  Lib/unittest/test/test_discovery.py           |  80 +++++++++-
  Lib/unittest/test/testmock/testmock.py        |  17 +-
  Lib/zipfile.py                                |   8 +-
  Misc/ACKS                                     |   1 +
  Misc/NEWS                                     |   9 +
  Misc/python-wing5.wpr                         |  18 ++
  Modules/posixmodule.c                         |   9 +-
  Modules/timemodule.c                          |   2 +-
  16 files changed, 197 insertions(+), 57 deletions(-)


diff --git a/Doc/library/zipfile.rst b/Doc/library/zipfile.rst
--- a/Doc/library/zipfile.rst
+++ b/Doc/library/zipfile.rst
@@ -130,7 +130,7 @@
 ---------------
 
 
-.. class:: ZipFile(file, mode='r', compression=ZIP_STORED, allowZip64=False)
+.. class:: ZipFile(file, mode='r', compression=ZIP_STORED, allowZip64=True)
 
    Open a ZIP file, where *file* can be either a path to a file (a string) or a
    file-like object.  The *mode* parameter should be ``'r'`` to read an existing
@@ -147,12 +147,9 @@
    :const:`ZIP_BZIP2` or :const:`ZIP_LZMA` is specified but the corresponding module
    (:mod:`zlib`, :mod:`bz2` or :mod:`lzma`) is not available, :exc:`RuntimeError`
    is also raised. The default is :const:`ZIP_STORED`.  If *allowZip64* is
-   ``True`` zipfile will create ZIP files that use the ZIP64 extensions when
-   the zipfile is larger than 2 GiB. If it is  false (the default) :mod:`zipfile`
+   ``True`` (the default) zipfile will create ZIP files that use the ZIP64
+   extensions when the zipfile is larger than 2 GiB. If it is  false :mod:`zipfile`
    will raise an exception when the ZIP file would require ZIP64 extensions.
-   ZIP64 extensions are disabled by default because the default :program:`zip`
-   and :program:`unzip` commands on Unix (the InfoZIP utilities) don't support
-   these extensions.
 
    If the file is created with mode ``'a'`` or ``'w'`` and then
    :meth:`closed <close>` without adding any files to the archive, the appropriate
@@ -171,6 +168,9 @@
    .. versionchanged:: 3.3
       Added support for :mod:`bzip2 <bz2>` and :mod:`lzma` compression.
 
+   .. versionchanged:: 3.4
+      ZIP64 extensions are enabled by default.
+
 
 .. method:: ZipFile.close()
 
@@ -374,12 +374,15 @@
 The :class:`PyZipFile` constructor takes the same parameters as the
 :class:`ZipFile` constructor, and one additional parameter, *optimize*.
 
-.. class:: PyZipFile(file, mode='r', compression=ZIP_STORED, allowZip64=False, \
+.. class:: PyZipFile(file, mode='r', compression=ZIP_STORED, allowZip64=True, \
                      optimize=-1)
 
    .. versionadded:: 3.2
       The *optimize* parameter.
 
+   .. versionchanged:: 3.4
+      ZIP64 extensions are enabled by default.
+
    Instances have one method in addition to those of :class:`ZipFile` objects:
 
    .. method:: PyZipFile.writepy(pathname, basename='', filterfunc=None)
diff --git a/Lib/pathlib.py b/Lib/pathlib.py
--- a/Lib/pathlib.py
+++ b/Lib/pathlib.py
@@ -6,7 +6,6 @@
 import posixpath
 import re
 import sys
-import time
 import weakref
 try:
     import threading
@@ -1076,9 +1075,8 @@
             # First try to bump modification time
             # Implementation note: GNU touch uses the UTIME_NOW option of
             # the utimensat() / futimens() functions.
-            t = time.time()
             try:
-                self._accessor.utime(self, (t, t))
+                self._accessor.utime(self, None)
             except OSError:
                 # Avoid exception chaining
                 pass
diff --git a/Lib/test/test_pathlib.py b/Lib/test/test_pathlib.py
--- a/Lib/test/test_pathlib.py
+++ b/Lib/test/test_pathlib.py
@@ -1391,11 +1391,8 @@
         # The file mtime should be refreshed by calling touch() again
         p.touch()
         st = p.stat()
-        # Issue #19715: there can be an inconsistency under Windows between
-        # the timestamp rounding when creating a file, and the timestamp
-        # rounding done when calling utime().  `delta` makes up for this.
-        delta = 1e-6 if os.name == 'nt' else 0
-        self.assertGreaterEqual(st.st_mtime, old_mtime - delta)
+        self.assertGreaterEqual(st.st_mtime_ns, old_mtime_ns)
+        self.assertGreaterEqual(st.st_mtime, old_mtime)
         # Now with exist_ok=False
         p = P / 'newfileB'
         self.assertFalse(p.exists())
@@ -1403,6 +1400,13 @@
         self.assertTrue(p.exists())
         self.assertRaises(OSError, p.touch, exist_ok=False)
 
+    def test_touch_nochange(self):
+        P = self.cls(BASE)
+        p = P / 'fileA'
+        p.touch()
+        with p.open('rb') as f:
+            self.assertEqual(f.read().strip(), b"this is file A")
+
     def test_mkdir(self):
         P = self.cls(BASE)
         p = P / 'newdirA'
diff --git a/Lib/test/test_zipfile.py b/Lib/test/test_zipfile.py
--- a/Lib/test/test_zipfile.py
+++ b/Lib/test/test_zipfile.py
@@ -506,12 +506,12 @@
     compression = zipfile.ZIP_STORED
 
     def large_file_exception_test(self, f, compression):
-        with zipfile.ZipFile(f, "w", compression) as zipfp:
+        with zipfile.ZipFile(f, "w", compression, allowZip64=False) as zipfp:
             self.assertRaises(zipfile.LargeZipFile,
                               zipfp.write, TESTFN, "another.name")
 
     def large_file_exception_test2(self, f, compression):
-        with zipfile.ZipFile(f, "w", compression) as zipfp:
+        with zipfile.ZipFile(f, "w", compression, allowZip64=False) as zipfp:
             self.assertRaises(zipfile.LargeZipFile,
                               zipfp.writestr, "another.name", self.data)
 
diff --git a/Lib/test/test_zipfile64.py b/Lib/test/test_zipfile64.py
--- a/Lib/test/test_zipfile64.py
+++ b/Lib/test/test_zipfile64.py
@@ -38,7 +38,7 @@
 
     def zipTest(self, f, compression):
         # Create the ZIP archive.
-        zipfp = zipfile.ZipFile(f, "w", compression, allowZip64=True)
+        zipfp = zipfile.ZipFile(f, "w", compression)
 
         # It will contain enough copies of self.data to reach about 6GB of
         # raw data to store.
@@ -92,7 +92,7 @@
     def testMoreThan64kFiles(self):
         # This test checks that more than 64k files can be added to an archive,
         # and that the resulting archive can be read properly by ZipFile
-        zipf = zipfile.ZipFile(TESTFN, mode="w")
+        zipf = zipfile.ZipFile(TESTFN, mode="w", allowZip64=False)
         zipf.debug = 100
         numfiles = (1 << 16) * 3//2
         for i in range(numfiles):
diff --git a/Lib/tkinter/test/test_tkinter/test_widgets.py b/Lib/tkinter/test/test_tkinter/test_widgets.py
--- a/Lib/tkinter/test/test_tkinter/test_widgets.py
+++ b/Lib/tkinter/test/test_tkinter/test_widgets.py
@@ -1,6 +1,7 @@
 import unittest
 import tkinter
 import os
+import sys
 from test.support import requires
 
 from tkinter.test.support import (tcl_version, requires_tcl,
@@ -262,6 +263,8 @@
 
     test_highlightthickness = StandardOptionsTests.test_highlightthickness
 
+    @unittest.skipIf(sys.platform == 'darwin',
+                     'crashes with Cocoa Tk (issue19733)')
     def test_image(self):
         widget = self.create()
         image = tkinter.PhotoImage('image1')
diff --git a/Lib/tkinter/test/widget_tests.py b/Lib/tkinter/test/widget_tests.py
--- a/Lib/tkinter/test/widget_tests.py
+++ b/Lib/tkinter/test/widget_tests.py
@@ -1,5 +1,7 @@
 # Common tests for test_tkinter/test_widgets.py and test_ttk/test_widgets.py
 
+import unittest
+import sys
 import tkinter
 from tkinter.ttk import setup_master, Scale
 from tkinter.test.support import (tcl_version, requires_tcl, get_tk_patchlevel,
@@ -289,6 +291,8 @@
         self.checkParam(widget, 'highlightthickness', -2, expected=0,
                         conv=self._conv_pixels)
 
+    @unittest.skipIf(sys.platform == 'darwin',
+                     'crashes with Cocoa Tk (issue19733)')
     def test_image(self):
         widget = self.create()
         self.checkImageParam(widget, 'image')
diff --git a/Lib/unittest/loader.py b/Lib/unittest/loader.py
--- a/Lib/unittest/loader.py
+++ b/Lib/unittest/loader.py
@@ -61,8 +61,9 @@
     def loadTestsFromTestCase(self, testCaseClass):
         """Return a suite of all tests cases contained in testCaseClass"""
         if issubclass(testCaseClass, suite.TestSuite):
-            raise TypeError("Test cases should not be derived from TestSuite." \
-                                " Maybe you meant to derive from TestCase?")
+            raise TypeError("Test cases should not be derived from "
+                            "TestSuite. Maybe you meant to derive from "
+                            "TestCase?")
         testCaseNames = self.getTestCaseNames(testCaseClass)
         if not testCaseNames and hasattr(testCaseClass, 'runTest'):
             testCaseNames = ['runTest']
@@ -200,6 +201,8 @@
         self._top_level_dir = top_level_dir
 
         is_not_importable = False
+        is_namespace = False
+        tests = []
         if os.path.isdir(os.path.abspath(start_dir)):
             start_dir = os.path.abspath(start_dir)
             if start_dir != top_level_dir:
@@ -213,15 +216,52 @@
             else:
                 the_module = sys.modules[start_dir]
                 top_part = start_dir.split('.')[0]
-                start_dir = os.path.abspath(os.path.dirname((the_module.__file__)))
+                try:
+                    start_dir = os.path.abspath(
+                       os.path.dirname((the_module.__file__)))
+                except AttributeError:
+                    # look for namespace packages
+                    try:
+                        spec = the_module.__spec__
+                    except AttributeError:
+                        spec = None
+
+                    if spec and spec.loader is None:
+                        if spec.submodule_search_locations is not None:
+                            is_namespace = True
+
+                            for path in the_module.__path__:
+                                if (not set_implicit_top and
+                                    not path.startswith(top_level_dir)):
+                                    continue
+                                self._top_level_dir = \
+                                    (path.split(the_module.__name__
+                                         .replace(".", os.path.sep))[0])
+                                tests.extend(self._find_tests(path,
+                                                              pattern,
+                                                              namespace=True))
+                    elif the_module.__name__ in sys.builtin_module_names:
+                        # builtin module
+                        raise TypeError('Can not use builtin modules '
+                                        'as dotted module names') from None
+                    else:
+                        raise TypeError(
+                            'don\'t know how to discover from {!r}'
+                            .format(the_module)) from None
+
                 if set_implicit_top:
-                    self._top_level_dir = self._get_directory_containing_module(top_part)
-                    sys.path.remove(top_level_dir)
+                    if not is_namespace:
+                        self._top_level_dir = \
+                           self._get_directory_containing_module(top_part)
+                        sys.path.remove(top_level_dir)
+                    else:
+                        sys.path.remove(top_level_dir)
 
         if is_not_importable:
             raise ImportError('Start directory is not importable: %r' % start_dir)
 
-        tests = list(self._find_tests(start_dir, pattern))
+        if not is_namespace:
+            tests = list(self._find_tests(start_dir, pattern))
         return self.suiteClass(tests)
 
     def _get_directory_containing_module(self, module_name):
@@ -254,7 +294,7 @@
         # override this method to use alternative matching strategy
         return fnmatch(path, pattern)
 
-    def _find_tests(self, start_dir, pattern):
+    def _find_tests(self, start_dir, pattern, namespace=False):
         """Used by discovery. Yields test suites it loads."""
         paths = sorted(os.listdir(start_dir))
 
@@ -287,7 +327,8 @@
                         raise ImportError(msg % (mod_name, module_dir, expected_dir))
                     yield self.loadTestsFromModule(module)
             elif os.path.isdir(full_path):
-                if not os.path.isfile(os.path.join(full_path, '__init__.py')):
+                if (not namespace and
+                    not os.path.isfile(os.path.join(full_path, '__init__.py'))):
                     continue
 
                 load_tests = None
@@ -304,7 +345,8 @@
                         # tests loaded from package file
                         yield tests
                     # recurse into the package
-                    yield from self._find_tests(full_path, pattern)
+                    yield from self._find_tests(full_path, pattern,
+                                                namespace=namespace)
                 else:
                     try:
                         yield load_tests(self, tests, pattern)
diff --git a/Lib/unittest/test/test_discovery.py b/Lib/unittest/test/test_discovery.py
--- a/Lib/unittest/test/test_discovery.py
+++ b/Lib/unittest/test/test_discovery.py
@@ -1,6 +1,8 @@
 import os
 import re
 import sys
+import types
+import builtins
 from test import support
 
 import unittest
@@ -173,7 +175,7 @@
         self.addCleanup(restore_isdir)
 
         _find_tests_args = []
-        def _find_tests(start_dir, pattern):
+        def _find_tests(start_dir, pattern, namespace=None):
             _find_tests_args.append((start_dir, pattern))
             return ['tests']
         loader._find_tests = _find_tests
@@ -436,7 +438,7 @@
         expectedPath = os.path.abspath(os.path.dirname(unittest.test.__file__))
 
         self.wasRun = False
-        def _find_tests(start_dir, pattern):
+        def _find_tests(start_dir, pattern, namespace=None):
             self.wasRun = True
             self.assertEqual(start_dir, expectedPath)
             return tests
@@ -446,5 +448,79 @@
         self.assertEqual(suite._tests, tests)
 
 
+    def test_discovery_from_dotted_path_builtin_modules(self):
+
+        loader = unittest.TestLoader()
+
+        listdir = os.listdir
+        os.listdir = lambda _: ['test_this_does_not_exist.py']
+        isfile = os.path.isfile
+        isdir = os.path.isdir
+        os.path.isdir = lambda _: False
+        orig_sys_path = sys.path[:]
+        def restore():
+            os.path.isfile = isfile
+            os.path.isdir = isdir
+            os.listdir = listdir
+            sys.path[:] = orig_sys_path
+        self.addCleanup(restore)
+
+        with self.assertRaises(TypeError) as cm:
+            loader.discover('sys')
+        self.assertEqual(str(cm.exception),
+                         'Can not use builtin modules '
+                         'as dotted module names')
+
+    def test_discovery_from_dotted_namespace_packages(self):
+        loader = unittest.TestLoader()
+
+        orig_import = __import__
+        package = types.ModuleType('package')
+        package.__path__ = ['/a', '/b']
+        package.__spec__ = types.SimpleNamespace(
+           loader=None,
+           submodule_search_locations=['/a', '/b']
+        )
+
+        def _import(packagename, *args, **kwargs):
+            sys.modules[packagename] = package
+            return package
+
+        def cleanup():
+            builtins.__import__ = orig_import
+        self.addCleanup(cleanup)
+        builtins.__import__ = _import
+
+        _find_tests_args = []
+        def _find_tests(start_dir, pattern, namespace=None):
+            _find_tests_args.append((start_dir, pattern))
+            return ['%s/tests' % start_dir]
+
+        loader._find_tests = _find_tests
+        loader.suiteClass = list
+        suite = loader.discover('package')
+        self.assertEqual(suite, ['/a/tests', '/b/tests'])
+
+    def test_discovery_failed_discovery(self):
+        loader = unittest.TestLoader()
+        package = types.ModuleType('package')
+        orig_import = __import__
+
+        def _import(packagename, *args, **kwargs):
+            sys.modules[packagename] = package
+            return package
+
+        def cleanup():
+            builtins.__import__ = orig_import
+        self.addCleanup(cleanup)
+        builtins.__import__ = _import
+
+        with self.assertRaises(TypeError) as cm:
+            loader.discover('package')
+        self.assertEqual(str(cm.exception),
+                         'don\'t know how to discover from {!r}'
+                         .format(package))
+
+
 if __name__ == '__main__':
     unittest.main()
diff --git a/Lib/unittest/test/testmock/testmock.py b/Lib/unittest/test/testmock/testmock.py
--- a/Lib/unittest/test/testmock/testmock.py
+++ b/Lib/unittest/test/testmock/testmock.py
@@ -1302,20 +1302,6 @@
             self.assertEqual(m.method_calls, [])
 
 
-    def test_attribute_deletion(self):
-        # this behaviour isn't *useful*, but at least it's now tested...
-        for Klass in Mock, MagicMock, NonCallableMagicMock, NonCallableMock:
-            m = Klass()
-            original = m.foo
-            m.foo = 3
-            del m.foo
-            self.assertEqual(m.foo, original)
-
-            new = m.foo = Mock()
-            del m.foo
-            self.assertEqual(m.foo, new)
-
-
     def test_mock_parents(self):
         for Klass in Mock, MagicMock:
             m = Klass()
@@ -1379,7 +1365,8 @@
 
 
     def test_attribute_deletion(self):
-        for mock in Mock(), MagicMock():
+        for mock in (Mock(), MagicMock(), NonCallableMagicMock(),
+                     NonCallableMock()):
             self.assertTrue(hasattr(mock, 'm'))
 
             del mock.m
diff --git a/Lib/zipfile.py b/Lib/zipfile.py
--- a/Lib/zipfile.py
+++ b/Lib/zipfile.py
@@ -876,7 +876,7 @@
 class ZipFile:
     """ Class with methods to open, read, write, close, list zip files.
 
-    z = ZipFile(file, mode="r", compression=ZIP_STORED, allowZip64=False)
+    z = ZipFile(file, mode="r", compression=ZIP_STORED, allowZip64=True)
 
     file: Either the path to the file, or a file-like object.
           If it is a path, the file will be opened and closed by ZipFile.
@@ -892,7 +892,7 @@
     fp = None                   # Set here since __del__ checks it
     _windows_illegal_name_trans_table = None
 
-    def __init__(self, file, mode="r", compression=ZIP_STORED, allowZip64=False):
+    def __init__(self, file, mode="r", compression=ZIP_STORED, allowZip64=True):
         """Open the ZIP file with mode read "r", write "w" or append "a"."""
         if mode not in ("r", "w", "a"):
             raise RuntimeError('ZipFile() requires mode "r", "w", or "a"')
@@ -1561,7 +1561,7 @@
     """Class to create ZIP archives with Python library files and packages."""
 
     def __init__(self, file, mode="r", compression=ZIP_STORED,
-                 allowZip64=False, optimize=-1):
+                 allowZip64=True, optimize=-1):
         ZipFile.__init__(self, file, mode=mode, compression=compression,
                          allowZip64=allowZip64)
         self._optimize = optimize
@@ -1783,7 +1783,7 @@
                              os.path.join(path, nm), os.path.join(zippath, nm))
             # else: ignore
 
-        with ZipFile(args[1], 'w', allowZip64=True) as zf:
+        with ZipFile(args[1], 'w') as zf:
             for src in args[2:]:
                 addToZip(zf, src, os.path.basename(src))
 
diff --git a/Misc/ACKS b/Misc/ACKS
--- a/Misc/ACKS
+++ b/Misc/ACKS
@@ -806,6 +806,7 @@
 Grzegorz Makarewicz
 David Malcolm
 Greg Malcolm
+William Mallard
 Ken Manheimer
 Vladimir Marangozov
 Colin Marc
diff --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -71,6 +71,12 @@
 - Issue #19689: Add ssl.create_default_context() factory function. It creates
   a new SSLContext object with secure default settings.
 
+- Issue #19727: os.utime(..., None) is now potentially more precise
+  under Windows.
+
+- Issue #17201: ZIP64 extensions now are enabled by default.  Patch by
+  William Mallard.
+
 - Issue #19292: Add SSLContext.load_default_certs() to load default root CA
   certificates from default stores or system stores. By default the method
   loads CA certs for authentication of server certs.
@@ -482,6 +488,9 @@
 Library
 -------
 
+- Issue #17457: unittest test discovery now works with namespace packages.
+  Patch by Claudiu Popa.
+
 - Issue #18235: Fix the sysconfig variables LDSHARED and BLDSHARED under AIX.
   Patch by David Edelsohn.
 
diff --git a/Misc/python-wing5.wpr b/Misc/python-wing5.wpr
new file mode 100644
--- /dev/null
+++ b/Misc/python-wing5.wpr
@@ -0,0 +1,18 @@
+#!wing
+#!version=5.0
+##################################################################
+# Wing IDE project file                                          #
+##################################################################
+[project attributes]
+proj.directory-list = [{'dirloc': loc('..'),
+                        'excludes': [u'.hg',
+                                     u'Lib/unittest/__pycache__',
+                                     u'Lib/unittest/test/__pycache__',
+                                     u'Lib/__pycache__',
+                                     u'build',
+                                     u'Doc/build'],
+                        'filter': '*',
+                        'include_hidden': False,
+                        'recursive': True,
+                        'watch_for_changes': True}]
+proj.file-type = 'shared'
diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c
--- a/Modules/posixmodule.c
+++ b/Modules/posixmodule.c
@@ -4953,13 +4953,8 @@
     }
 
     if (utime.now) {
-        SYSTEMTIME now;
-        GetSystemTime(&now);
-        if (!SystemTimeToFileTime(&now, &mtime) ||
-            !SystemTimeToFileTime(&now, &atime)) {
-            PyErr_SetFromWindowsErr(0);
-            goto exit;
-        }
+        GetSystemTimeAsFileTime(&mtime);
+        atime = mtime;
     }
     else {
         time_t_to_FILE_TIME(utime.atime_s, utime.atime_ns, &atime);
diff --git a/Modules/timemodule.c b/Modules/timemodule.c
--- a/Modules/timemodule.c
+++ b/Modules/timemodule.c
@@ -650,7 +650,7 @@
             return NULL;
         }
     }
-#elif defined(_AIX) && defined(HAVE_WCSFTIME)
+#elif (defined(_AIX) || defined(sun)) && defined(HAVE_WCSFTIME)
     for(outbuf = wcschr(fmt, '%');
         outbuf != NULL;
         outbuf = wcschr(outbuf+2, '%'))

-- 
Repository URL: http://hg.python.org/cpython


More information about the Python-checkins mailing list