From 4a24283272fcd646dc89ab6ab00c2e919bc5a773 Mon Sep 17 00:00:00 2001 From: "Jason R. Coombs" Date: Sun, 15 Jan 2012 11:45:27 -0500 Subject: [PATCH] Adding regression test for issue6727 --- Lib/test/test_import.py | 118 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 116 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_import.py b/Lib/test/test_import.py index e426290df3a..32d6e12143a 100644 --- a/Lib/test/test_import.py +++ b/Lib/test/test_import.py @@ -7,9 +7,11 @@ import random import stat import sys import unittest +import textwrap +import shutil + from test.test_support import (unlink, TESTFN, unload, run_unittest, rmtree, is_jython, check_warnings, EnvironmentVarGuard) -import textwrap from test import script_helper def remove_files(name): @@ -488,8 +490,120 @@ class RelativeImportTests(unittest.TestCase): "implicit absolute import") +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, PycRewritingTests, PathsTests, RelativeImportTests) + run_unittest(ImportTests, PycRewritingTests, PathsTests, + RelativeImportTests, TestSymbolicallyLinkedPackage) if __name__ == '__main__': # Test needs to be a package, so we can do relative imports.