Commits

Jason R. Coombs committed 98f6f9b

Adding regression test for issue6727

  • Participants
  • Parent commits 7ba2601
  • Branches 3.2

Comments (0)

Files changed (1)

File Lib/test/test_import.py

 import unittest
 import textwrap
 import errno
+import shutil
 
 from test.support import (
     EnvironmentVarGuard, TESTFN, check_warnings, forget, is_jython,
         importlib_util.using___import__ = self._importlib_util_flag
 
 
+class TestSymbolicallyLinkedPackage(unittest.TestCase):
+    package_name = 'sample'
+
+    def setUp(self):
+        if os.path.exists('sample-tagged'): shutil.rmtree('sample-tagged')
+        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)
+        init_file = os.path.join(self.tagged, '__init__.py')
+        open(init_file, 'w').close()
+        assert os.path.exists(init_file)
+
+        # now create a symlink to the tagged package
+        # sample -> sample-tagged
+        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'))
+
+    @property
+    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")
+    def test_symlinked_dir_importable(self):
+        # make sure sample can only be imported from the current directory.
+        sys.path[:] = ['.']
+
+        # and try to import the package
+        pkg = __import__(self.package_name)
+
+    def tearDown(self):
+        # now cleanup
+        if os.path.exists(self.package_name):
+            os.rmdir(self.package_name)
+        if os.path.exists(self.tagged):
+            shutil.rmtree(self.tagged)
+        sys.path[:] = self.orig_sys_path
+
+
 def test_main(verbose=None):
     run_unittest(ImportTests, PycacheTests,
                  PycRewritingTests, PathsTests, RelativeImportTests,
                  OverridingImportBuiltinTests,
-                 RelativeImportFromImportlibTests)
+                 RelativeImportFromImportlibTests,
+                 TestSymbolicallyLinkedPackage)
 
 
 if __name__ == '__main__':