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).
This commit is contained in:
parent
ea4629afa6
commit
e107ab3b6c
|
@ -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)
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue