diff options
Diffstat (limited to 'lib/python2.7/test/symlink_support.py')
-rw-r--r-- | lib/python2.7/test/symlink_support.py | 100 |
1 files changed, 100 insertions, 0 deletions
diff --git a/lib/python2.7/test/symlink_support.py b/lib/python2.7/test/symlink_support.py new file mode 100644 index 0000000..301d0f9 --- /dev/null +++ b/lib/python2.7/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) |