mirror of
				https://github.com/python/cpython.git
				synced 2025-11-04 03:44:55 +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)
 |