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

barry.warsaw python-checkins at python.org
Mon Jun 4 15:52:01 CEST 2012


http://hg.python.org/cpython/rev/4b83cb7d1e81
changeset:   77340:4b83cb7d1e81
parent:      77339:9c445f4695c1
parent:      77338:6808a72fc9ec
user:        Barry Warsaw <barry at python.org>
date:        Mon Jun 04 09:41:48 2012 -0400
summary:
  Trunk merge.

files:
  Doc/library/functools.rst  |   5 +-
  Include/pyerrors.h         |   2 +-
  Lib/functools.py           |  43 ++++++++++++++-----------
  Lib/ipaddress.py           |   2 +-
  Lib/os.py                  |  16 ++++++++-
  Lib/test/test_ipaddress.py |   1 +
  Lib/test/test_os.py        |  25 +++++++++++++++
  Misc/NEWS                  |   5 +++
  8 files changed, 74 insertions(+), 25 deletions(-)


diff --git a/Doc/library/functools.rst b/Doc/library/functools.rst
--- a/Doc/library/functools.rst
+++ b/Doc/library/functools.rst
@@ -49,8 +49,9 @@
    Since a dictionary is used to cache results, the positional and keyword
    arguments to the function must be hashable.
 
-   If *maxsize* is set to None, the LRU feature is disabled and the cache
-   can grow without bound.
+   If *maxsize* is set to None, the LRU feature is disabled and the cache can
+   grow without bound.  The LRU feature performs best when *maxsize* is a
+   power-of-two.
 
    If *typed* is set to True, function arguments of different types will be
    cached separately.  For example, ``f(3)`` and ``f(3.0)`` will be treated
diff --git a/Include/pyerrors.h b/Include/pyerrors.h
--- a/Include/pyerrors.h
+++ b/Include/pyerrors.h
@@ -87,7 +87,7 @@
 PyAPI_FUNC(void) PyErr_SetExcInfo(PyObject *, PyObject *, PyObject *);
 
 #if defined(__clang__) || \
-    (defined(__GNUC__) && \
+    (defined(__GNUC_MAJOR__) && \
      ((__GNUC_MAJOR__ >= 3) || \
       (__GNUC_MAJOR__ == 2) && (__GNUC_MINOR__ >= 5)))
 #define _Py_NO_RETURN __attribute__((__noreturn__))
diff --git a/Lib/functools.py b/Lib/functools.py
--- a/Lib/functools.py
+++ b/Lib/functools.py
@@ -142,30 +142,35 @@
 
 _CacheInfo = namedtuple("CacheInfo", ["hits", "misses", "maxsize", "currsize"])
 
-class _CacheKey(list):
-    'Make a cache key from optionally typed positional and keyword arguments'
-
+class _HashedSeq(list):
     __slots__ = 'hashvalue'
 
-    def __init__(self, args, kwds, typed,
-                 kwd_mark = (object(),),
-                 sorted=sorted, tuple=tuple, type=type, hash=hash):
-        key = args
-        if kwds:
-            sorted_items = sorted(kwds.items())
-            key += kwd_mark
-            for item in sorted_items:
-                key += item
-        if typed:
-            key += tuple(type(v) for v in args)
-            if kwds:
-                key += tuple(type(v) for k, v in sorted_items)
-        self[:] = key
-        self.hashvalue = hash(key)  # so we only have to hash just once
+    def __init__(self, tup, hash=hash):
+        self[:] = tup
+        self.hashvalue = hash(tup)
 
     def __hash__(self):
         return self.hashvalue
 
+def _make_key(args, kwds, typed,
+             kwd_mark = (object(),),
+             fasttypes = {int, str, frozenset, type(None)},
+             sorted=sorted, tuple=tuple, type=type, len=len):
+    'Make a cache key from optionally typed positional and keyword arguments'
+    key = args
+    if kwds:
+        sorted_items = sorted(kwds.items())
+        key += kwd_mark
+        for item in sorted_items:
+            key += item
+    if typed:
+        key += tuple(type(v) for v in args)
+        if kwds:
+            key += tuple(type(v) for k, v in sorted_items)
+    elif len(key) == 1 and type(key[0]) in fasttypes:
+        return key[0]
+    return _HashedSeq(key)
+
 def lru_cache(maxsize=128, typed=False):
     """Least-recently-used cache decorator.
 
@@ -193,7 +198,7 @@
 
     # Constants shared by all lru cache instances:
     sentinel = object()          # unique object used to signal cache misses
-    make_key = _CacheKey         # build a key from the function arguments
+    make_key = _make_key         # build a key from the function arguments
     PREV, NEXT, KEY, RESULT = 0, 1, 2, 3   # names for the link fields
 
     def decorating_function(user_function):
diff --git a/Lib/ipaddress.py b/Lib/ipaddress.py
--- a/Lib/ipaddress.py
+++ b/Lib/ipaddress.py
@@ -1130,7 +1130,7 @@
         """
         unspecified_address = IPv4Address('0.0.0.0')
         if isinstance(self, _BaseAddress):
-            return self in unspecified_address
+            return self == unspecified_address
         return (self.network_address == self.broadcast_address ==
                 unspecified_address)
 
diff --git a/Lib/os.py b/Lib/os.py
--- a/Lib/os.py
+++ b/Lib/os.py
@@ -160,8 +160,20 @@
     try:
         mkdir(name, mode)
     except OSError as e:
-        if not (e.errno == errno.EEXIST and exist_ok and path.isdir(name) and
-                st.S_IMODE(lstat(name).st_mode) == _get_masked_mode(mode)):
+        dir_exists = path.isdir(name)
+        expected_mode = _get_masked_mode(mode)
+        if dir_exists:
+            # S_ISGID is automatically copied by the OS from parent to child
+            # directories on mkdir.  Don't consider it being set to be a mode
+            # mismatch as mkdir does not unset it when not specified in mode.
+            actual_mode = st.S_IMODE(lstat(name).st_mode) & ~st.S_ISGID
+        else:
+            actual_mode = -1
+        if not (e.errno == errno.EEXIST and exist_ok and dir_exists and
+                actual_mode == expected_mode):
+            if dir_exists and actual_mode != expected_mode:
+                e.strerror += ' (mode %o != expected mode %o)' % (
+                        actual_mode, expected_mode)
             raise
 
 def removedirs(name):
diff --git a/Lib/test/test_ipaddress.py b/Lib/test/test_ipaddress.py
--- a/Lib/test/test_ipaddress.py
+++ b/Lib/test/test_ipaddress.py
@@ -837,6 +837,7 @@
         self.assertEqual(False, ipaddress.ip_network('128.0.0.0').is_loopback)
 
         # test addresses
+        self.assertEqual(True, ipaddress.ip_address('0.0.0.0').is_unspecified)
         self.assertEqual(True, ipaddress.ip_address('224.1.1.1').is_multicast)
         self.assertEqual(False, ipaddress.ip_address('240.0.0.0').is_multicast)
 
diff --git a/Lib/test/test_os.py b/Lib/test/test_os.py
--- a/Lib/test/test_os.py
+++ b/Lib/test/test_os.py
@@ -838,6 +838,31 @@
         os.makedirs(path, mode=mode, exist_ok=True)
         os.umask(old_mask)
 
+    def test_exist_ok_s_isgid_directory(self):
+        path = os.path.join(support.TESTFN, 'dir1')
+        S_ISGID = stat.S_ISGID
+        mode = 0o777
+        old_mask = os.umask(0o022)
+        try:
+            existing_testfn_mode = stat.S_IMODE(
+                    os.lstat(support.TESTFN).st_mode)
+            os.chmod(support.TESTFN, existing_testfn_mode | S_ISGID)
+            if (os.lstat(support.TESTFN).st_mode & S_ISGID != S_ISGID):
+                raise unittest.SkipTest('No support for S_ISGID dir mode.')
+            # The os should apply S_ISGID from the parent dir for us, but
+            # this test need not depend on that behavior.  Be explicit.
+            os.makedirs(path, mode | S_ISGID)
+            # http://bugs.python.org/issue14992
+            # Should not fail when the bit is already set.
+            os.makedirs(path, mode, exist_ok=True)
+            # remove the bit.
+            os.chmod(path, stat.S_IMODE(os.lstat(path).st_mode) & ~S_ISGID)
+            with self.assertRaises(OSError):
+                # Should fail when the bit is not already set when demanded.
+                os.makedirs(path, mode | S_ISGID, exist_ok=True)
+        finally:
+            os.umask(old_mask)
+
     def test_exist_ok_existing_regular_file(self):
         base = support.TESTFN
         path = os.path.join(support.TESTFN, 'dir1')
diff --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -10,6 +10,11 @@
 Library
 -------
 
+- Issue #14992: os.makedirs(path, exist_ok=True) would raise an OSError
+  when the path existed and had the S_ISGID mode bit set when it was
+  not explicitly asked for.  This is no longer an exception as mkdir
+  cannot control if the OS sets that bit for it or not.
+
 - Issue #14989: Make the CGI enable option to http.server available via command
   line.
 

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


More information about the Python-checkins mailing list