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)