aboutsummaryrefslogtreecommitdiff
blob: b0457596d975c1855f070d6dbdea0fb9ebbbd5b6 (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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
"""
Support routines for subprocess module.
Currently, this extension module is only required when using the
subprocess module on Windows.
"""

import sys
if sys.platform != 'win32':
    raise ImportError("The '_subprocess' module is only available on Windows")

# Declare external Win32 functions

from _pypy_winbase_cffi import ffi as _ffi
_kernel32 = _ffi.dlopen('kernel32')

GetVersion = _kernel32.GetVersion


# Now the _subprocess module implementation

def _WinError():
    code, message = _ffi.getwinerror()
    raise WindowsError(code, message)

def _int2handle(val):
    return _ffi.cast("HANDLE", val)

_INVALID_HANDLE_VALUE = _int2handle(-1)

class _handle(object):
    def __init__(self, c_handle):
        # 'c_handle' is a cffi cdata of type HANDLE, which is basically 'void *'
        self.c_handle = c_handle
        if int(self) != -1:
            self.c_handle = _ffi.gc(self.c_handle, _kernel32.CloseHandle)

    def __int__(self):
        return int(_ffi.cast("intptr_t", self.c_handle))

    def __repr__(self):
        return '<_subprocess.handle %d at 0x%x>' % (int(self), id(self))

    def Detach(self):
        h = int(self)
        if h != -1:
            c_handle = self.c_handle
            self.c_handle = _INVALID_HANDLE_VALUE
            _ffi.gc(c_handle, None)
        return h

    def Close(self):
        if int(self) != -1:
            c_handle = self.c_handle
            self.c_handle = _INVALID_HANDLE_VALUE
            _ffi.gc(c_handle, None)
            _kernel32.CloseHandle(c_handle)

def CreatePipe(attributes, size):
    handles = _ffi.new("HANDLE[2]")

    res = _kernel32.CreatePipe(handles, handles + 1, _ffi.NULL, size)

    if not res:
        raise _WinError()

    return _handle(handles[0]), _handle(handles[1])

def GetCurrentProcess():
    return _handle(_kernel32.GetCurrentProcess())

def DuplicateHandle(source_process, source, target_process, access, inherit, options=0):
    # CPython: the first three arguments are expected to be integers
    target = _ffi.new("HANDLE[1]")

    res = _kernel32.DuplicateHandle(
        _int2handle(source_process),
        _int2handle(source),
        _int2handle(target_process),
        target, access, inherit, options)

    if not res:
        raise _WinError()

    return _handle(target[0])

def _z(input):
    if input is None:
        return _ffi.NULL
    if isinstance(input, basestring):
        return str(input)
    raise TypeError("string/unicode/None expected, got %r" % (
        type(input).__name__,))

def CreateProcess(name, command_line, process_attr, thread_attr,
                  inherit, flags, env, start_dir, startup_info):
    si = _ffi.new("STARTUPINFO *")
    if startup_info is not None:
        si.dwFlags = startup_info.dwFlags
        si.wShowWindow = startup_info.wShowWindow
        # CPython: these three handles are expected to be _handle objects
        if startup_info.hStdInput:
            si.hStdInput = startup_info.hStdInput.c_handle
        if startup_info.hStdOutput:
            si.hStdOutput = startup_info.hStdOutput.c_handle
        if startup_info.hStdError:
            si.hStdError = startup_info.hStdError.c_handle

    pi = _ffi.new("PROCESS_INFORMATION *")

    if env is not None:
        envbuf = ""
        for k, v in env.iteritems():
            envbuf += "%s=%s\0" % (k, v)
        envbuf += '\0'
    else:
        envbuf = _ffi.NULL

    res = _kernel32.CreateProcessA(_z(name), _z(command_line), _ffi.NULL,
                                   _ffi.NULL, inherit, flags, envbuf,
                                   _z(start_dir), si, pi)

    if not res:
        raise _WinError()

    return (_handle(pi.hProcess),
            _handle(pi.hThread),
            pi.dwProcessId,
            pi.dwThreadId)

def WaitForSingleObject(handle, milliseconds):
    # CPython: the first argument is expected to be an integer.
    res = _kernel32.WaitForSingleObject(_int2handle(handle), milliseconds)
    if res < 0:
        raise _WinError()

    return res

def GetExitCodeProcess(handle):
    # CPython: the first argument is expected to be an integer.
    code = _ffi.new("DWORD[1]")

    res = _kernel32.GetExitCodeProcess(_int2handle(handle), code)

    if not res:
        raise _WinError()

    return code[0]

def TerminateProcess(handle, exitcode):
    # CPython: the first argument is expected to be an integer.
    # The second argument is silently wrapped in a UINT.
    res = _kernel32.TerminateProcess(_int2handle(handle),
                                     _ffi.cast("UINT", exitcode))

    if not res:
        raise _WinError()

def GetStdHandle(stdhandle):
    stdhandle = _ffi.cast("DWORD", stdhandle)
    res = _kernel32.GetStdHandle(stdhandle)

    if not res:
        return None
    else:
        # note: returns integer, not handle object
        return int(_ffi.cast("intptr_t", res))

STD_INPUT_HANDLE = -10
STD_OUTPUT_HANDLE = -11
STD_ERROR_HANDLE = -12
DUPLICATE_SAME_ACCESS = 2
STARTF_USESTDHANDLES = 0x100
STARTF_USESHOWWINDOW = 0x001
SW_HIDE = 0
INFINITE = 0xffffffff
WAIT_OBJECT_0 = 0
CREATE_NEW_CONSOLE = 0x010
CREATE_NEW_PROCESS_GROUP = 0x200
STILL_ACTIVE = 259