mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 02:15:10 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			100 lines
		
	
	
	
		
			3.2 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			100 lines
		
	
	
	
		
			3.2 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| 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)
 | 
