1 import os 2 import unittest 3 import platform 4 5 from test.test_support import TESTFN 6 7 def can_symlink(): 8 # cache the result in can_symlink.prev_val 9 prev_val = getattr(can_symlink, 'prev_val', None) 10 if prev_val is not None: 11 return prev_val 12 symlink_path = TESTFN + "can_symlink" 13 try: 14 symlink(TESTFN, symlink_path) 15 can = True 16 except (OSError, NotImplementedError, AttributeError): 17 can = False 18 else: 19 os.remove(symlink_path) 20 can_symlink.prev_val = can 21 return can 22 23 def skip_unless_symlink(test): 24 """Skip decorator for tests that require functional symlink""" 25 ok = can_symlink() 26 msg = "Requires functional symlink implementation" 27 return test if ok else unittest.skip(msg)(test) 28 29 def _symlink_win32(target, link, target_is_directory=False): 30 """ 31 Ctypes symlink implementation since Python doesn't support 32 symlinks in windows yet. Borrowed from jaraco.windows project. 33 """ 34 import ctypes.wintypes 35 CreateSymbolicLink = ctypes.windll.kernel32.CreateSymbolicLinkW 36 CreateSymbolicLink.argtypes = ( 37 ctypes.wintypes.LPWSTR, 38 ctypes.wintypes.LPWSTR, 39 ctypes.wintypes.DWORD, 40 ) 41 CreateSymbolicLink.restype = ctypes.wintypes.BOOLEAN 42 43 def format_system_message(errno): 44 """ 45 Call FormatMessage with a system error number to retrieve 46 the descriptive error message. 47 """ 48 # first some flags used by FormatMessageW 49 ALLOCATE_BUFFER = 0x100 50 ARGUMENT_ARRAY = 0x2000 51 FROM_HMODULE = 0x800 52 FROM_STRING = 0x400 53 FROM_SYSTEM = 0x1000 54 IGNORE_INSERTS = 0x200 55 56 # Let FormatMessageW allocate the buffer (we'll free it below) 57 # Also, let it know we want a system error message. 58 flags = ALLOCATE_BUFFER | FROM_SYSTEM 59 source = None 60 message_id = errno 61 language_id = 0 62 result_buffer = ctypes.wintypes.LPWSTR() 63 buffer_size = 0 64 arguments = None 65 bytes = ctypes.windll.kernel32.FormatMessageW( 66 flags, 67 source, 68 message_id, 69 language_id, 70 ctypes.byref(result_buffer), 71 buffer_size, 72 arguments, 73 ) 74 # note the following will cause an infinite loop if GetLastError 75 # repeatedly returns an error that cannot be formatted, although 76 # this should not happen. 77 handle_nonzero_success(bytes) 78 message = result_buffer.value 79 ctypes.windll.kernel32.LocalFree(result_buffer) 80 return message 81 82 def handle_nonzero_success(result): 83 if result == 0: 84 value = ctypes.windll.kernel32.GetLastError() 85 strerror = format_system_message(value) 86 raise WindowsError(value, strerror) 87 88 target_is_directory = target_is_directory or os.path.isdir(target) 89 handle_nonzero_success(CreateSymbolicLink(link, target, target_is_directory)) 90 91 symlink = os.symlink if hasattr(os, 'symlink') else ( 92 _symlink_win32 if platform.system() == 'Windows' else None 93 ) 94 95 def remove_symlink(name): 96 # On Windows, to remove a directory symlink, one must use rmdir 97 try: 98 os.rmdir(name) 99 except OSError: 100 os.remove(name) 101