Commit e107ab3b authored by Jason R. Coombs's avatar Jason R. Coombs

Moved symlink support into its own module. Ported can_symlink from Python 3.2,...

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).
parent ea4629af
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)
......@@ -12,6 +12,7 @@ import shutil
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 @@ class TestSymbolicallyLinkedPackage(unittest.TestCase):
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 @@ class TestSymbolicallyLinkedPackage(unittest.TestCase):
# 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 @@ class TestSymbolicallyLinkedPackage(unittest.TestCase):
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 @@ class TestSymbolicallyLinkedPackage(unittest.TestCase):
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)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment