summaryrefslogtreecommitdiff
path: root/lib/python2.7/test/symlink_support.py
blob: 301d0f99a929b6b2484fa48cdebead690882c434 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
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)