[Python-checkins] cpython (2.7): Moved symlink support into its own module. Ported can_symlink from Python 3.2,

jason.coombs python-checkins at python.org
Fri Mar 9 05:11:39 CET 2012


http://hg.python.org/cpython/rev/f8efcca7e90f
changeset:   75500:f8efcca7e90f
branch:      2.7
parent:      75495:c3bd31d3c7f5
user:        Jason R. Coombs <jaraco at jaraco.com>
date:        Thu Mar 08 18:28:08 2012 -0500
summary:
  Moved symlink support into its own module. Ported can_symlink from Python 3.2, skipping symlink test when it cannot be invoked (such as when the symlink privilege is not present).

files:
  Lib/test/symlink_support.py |  100 ++++++++++++++++++++++++
  Lib/test/test_import.py     |   81 +------------------
  2 files changed, 105 insertions(+), 76 deletions(-)


diff --git a/Lib/test/symlink_support.py b/Lib/test/symlink_support.py
new file mode 100644
--- /dev/null
+++ b/Lib/test/symlink_support.py
@@ -0,0 +1,100 @@
+import os
+import unittest
+import platform
+
+from test.test_support import TESTFN
+
+def can_symlink():
+    # cache the result in can_symlink.prev_val
+    prev_val = getattr(can_symlink, 'prev_val', None)
+    if prev_val is not None:
+        return prev_val
+    symlink_path = TESTFN + "can_symlink"
+    try:
+        symlink(TESTFN, symlink_path)
+        can = True
+    except (OSError, NotImplementedError, AttributeError):
+        can = False
+    else:
+        os.remove(symlink_path)
+    can_symlink.prev_val = can
+    return can
+
+def skip_unless_symlink(test):
+    """Skip decorator for tests that require functional symlink"""
+    ok = can_symlink()
+    msg = "Requires functional symlink implementation"
+    return test if ok else unittest.skip(msg)(test)
+
+def _symlink_win32(target, link, target_is_directory=False):
+    """
+    Ctypes symlink implementation since Python doesn't support
+    symlinks in windows yet. Borrowed from jaraco.windows project.
+    """
+    import ctypes.wintypes
+    CreateSymbolicLink = ctypes.windll.kernel32.CreateSymbolicLinkW
+    CreateSymbolicLink.argtypes = (
+        ctypes.wintypes.LPWSTR,
+        ctypes.wintypes.LPWSTR,
+        ctypes.wintypes.DWORD,
+        )
+    CreateSymbolicLink.restype = ctypes.wintypes.BOOLEAN
+
+    def format_system_message(errno):
+        """
+        Call FormatMessage with a system error number to retrieve
+        the descriptive error message.
+        """
+        # first some flags used by FormatMessageW
+        ALLOCATE_BUFFER = 0x100
+        ARGUMENT_ARRAY = 0x2000
+        FROM_HMODULE = 0x800
+        FROM_STRING = 0x400
+        FROM_SYSTEM = 0x1000
+        IGNORE_INSERTS = 0x200
+
+        # Let FormatMessageW allocate the buffer (we'll free it below)
+        # Also, let it know we want a system error message.
+        flags = ALLOCATE_BUFFER | FROM_SYSTEM
+        source = None
+        message_id = errno
+        language_id = 0
+        result_buffer = ctypes.wintypes.LPWSTR()
+        buffer_size = 0
+        arguments = None
+        bytes = ctypes.windll.kernel32.FormatMessageW(
+            flags,
+            source,
+            message_id,
+            language_id,
+            ctypes.byref(result_buffer),
+            buffer_size,
+            arguments,
+            )
+        # note the following will cause an infinite loop if GetLastError
+        #  repeatedly returns an error that cannot be formatted, although
+        #  this should not happen.
+        handle_nonzero_success(bytes)
+        message = result_buffer.value
+        ctypes.windll.kernel32.LocalFree(result_buffer)
+        return message
+
+    def handle_nonzero_success(result):
+        if result == 0:
+            value = ctypes.windll.kernel32.GetLastError()
+            strerror = format_system_message(value)
+            raise WindowsError(value, strerror)
+
+    target_is_directory = target_is_directory or os.path.isdir(target)
+    handle_nonzero_success(CreateSymbolicLink(link, target, target_is_directory))
+
+symlink = os.symlink if hasattr(os, 'symlink') else (
+    _symlink_win32 if platform.system() == 'Windows' else None
+)
+
+def remove_symlink(name):
+    # On Windows, to remove a directory symlink, one must use rmdir
+    try:
+        os.rmdir(name)
+    except OSError:
+        os.remove(name)
diff --git a/Lib/test/test_import.py b/Lib/test/test_import.py
--- a/Lib/test/test_import.py
+++ b/Lib/test/test_import.py
@@ -12,6 +12,7 @@
 
 from test.test_support import (unlink, TESTFN, unload, run_unittest, rmtree,
                                is_jython, check_warnings, EnvironmentVarGuard)
+from test import symlink_support
 from test import script_helper
 
 def remove_files(name):
@@ -497,11 +498,9 @@
         if os.path.exists(self.tagged):
             shutil.rmtree(self.tagged)
         if os.path.exists(self.package_name):
-            self.remove_symlink(self.package_name)
+            symlink_support.remove_symlink(self.package_name)
         self.orig_sys_path = sys.path[:]
 
-        symlink = getattr(os, 'symlink', None) or self._symlink_win32
-
         # create a sample package; imagine you have a package with a tag and
         #  you want to symbolically link it from its untagged name.
         os.mkdir(self.tagged)
@@ -511,7 +510,7 @@
 
         # now create a symlink to the tagged package
         # sample -> sample-tagged
-        symlink(self.tagged, self.package_name)
+        symlink_support.symlink(self.tagged, self.package_name)
 
         assert os.path.isdir(self.package_name)
         assert os.path.isfile(os.path.join(self.package_name, '__init__.py'))
@@ -520,74 +519,12 @@
     def tagged(self):
         return self.package_name + '-tagged'
 
-    @classmethod
-    def _symlink_win32(cls, target, link, target_is_directory=False):
-        """
-        Ctypes symlink implementation since Python doesn't support
-        symlinks in windows yet. Borrowed from jaraco.windows project.
-        """
-        import ctypes.wintypes
-        CreateSymbolicLink = ctypes.windll.kernel32.CreateSymbolicLinkW
-        CreateSymbolicLink.argtypes = (
-            ctypes.wintypes.LPWSTR,
-            ctypes.wintypes.LPWSTR,
-            ctypes.wintypes.DWORD,
-            )
-        CreateSymbolicLink.restype = ctypes.wintypes.BOOLEAN
-
-        def format_system_message(errno):
-            """
-            Call FormatMessage with a system error number to retrieve
-            the descriptive error message.
-            """
-            # first some flags used by FormatMessageW
-            ALLOCATE_BUFFER = 0x100
-            ARGUMENT_ARRAY = 0x2000
-            FROM_HMODULE = 0x800
-            FROM_STRING = 0x400
-            FROM_SYSTEM = 0x1000
-            IGNORE_INSERTS = 0x200
-
-            # Let FormatMessageW allocate the buffer (we'll free it below)
-            # Also, let it know we want a system error message.
-            flags = ALLOCATE_BUFFER | FROM_SYSTEM
-            source = None
-            message_id = errno
-            language_id = 0
-            result_buffer = ctypes.wintypes.LPWSTR()
-            buffer_size = 0
-            arguments = None
-            bytes = ctypes.windll.kernel32.FormatMessageW(
-                flags,
-                source,
-                message_id,
-                language_id,
-                ctypes.byref(result_buffer),
-                buffer_size,
-                arguments,
-                )
-            # note the following will cause an infinite loop if GetLastError
-            #  repeatedly returns an error that cannot be formatted, although
-            #  this should not happen.
-            handle_nonzero_success(bytes)
-            message = result_buffer.value
-            ctypes.windll.kernel32.LocalFree(result_buffer)
-            return message
-
-        def handle_nonzero_success(result):
-            if result == 0:
-                value = ctypes.windll.kernel32.GetLastError()
-                strerror = format_system_message(value)
-                raise WindowsError(value, strerror)
-
-        target_is_directory = target_is_directory or os.path.isdir(target)
-        handle_nonzero_success(CreateSymbolicLink(link, target, target_is_directory))
-
     # regression test for issue6727
     @unittest.skipUnless(
         not hasattr(sys, 'getwindowsversion')
         or sys.getwindowsversion() >= (6, 0),
         "Windows Vista or later required")
+    @symlink_support.skip_unless_symlink
     def test_symlinked_dir_importable(self):
         # make sure sample can only be imported from the current directory.
         sys.path[:] = ['.']
@@ -598,19 +535,11 @@
     def tearDown(self):
         # now cleanup
         if os.path.exists(self.package_name):
-            self.remove_symlink(self.package_name)
+            symlink_support.remove_symlink(self.package_name)
         if os.path.exists(self.tagged):
             shutil.rmtree(self.tagged)
         sys.path[:] = self.orig_sys_path
 
-    @staticmethod
-    def remove_symlink(name):
-        # On Windows, to remove a directory symlink, one must use rmdir
-        try:
-            os.rmdir(name)
-        except OSError:
-            os.remove(name)
-
 def test_main(verbose=None):
     run_unittest(ImportTests, PycRewritingTests, PathsTests,
         RelativeImportTests, TestSymbolicallyLinkedPackage)

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


More information about the Python-checkins mailing list