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 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328
|
"""
Support routines for subprocess and multiprocess module.
Currently, this extension module is only required when using the
modules on Windows.
"""
import sys
if sys.platform != 'win32':
raise ImportError("The '_winapi' 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
NULL = _ffi.NULL
# Now the _subprocess module implementation
def _WinError():
code, message = _ffi.getwinerror()
raise WindowsError(code, message)
def _int2handle(val):
return _ffi.cast("HANDLE", val)
def _handle2int(handle):
return int(_ffi.cast("intptr_t", handle))
_INVALID_HANDLE_VALUE = _int2handle(-1)
def CreatePipe(attributes, size):
handles = _ffi.new("HANDLE[2]")
res = _kernel32.CreatePipe(handles, handles + 1, NULL, size)
if not res:
raise _WinError()
return _handle2int(handles[0]), _handle2int(handles[1])
def CreateNamedPipe(*args):
handle = _kernel32.CreateNamedPipeW(*args)
if handle == INVALID_HANDLE_VALUE:
raise _WinError()
return handle
def CreateFile(*args):
handle = _kernel32.CreateFileW(*args)
if handle == INVALID_HANDLE_VALUE:
raise _WinError()
return handle
def SetNamedPipeHandleState(namedpipe, mode, max_collection_count, collect_data_timeout):
d0 = _ffi.new('DWORD[1]', [mode])
if max_collection_count is None:
d1 = NULL
else:
d1 = _ffi.new('DWORD[1]', [max_collection_count])
if collect_data_timeout is None:
d2 = NULL
else:
d2 = _ffi.new('DWORD[1]', [collect_data_timeout])
ret = _kernel32.SetNamedPipeHandleState(namedpipe, d0, d1, d2)
if not ret:
raise _WinError()
class Overlapped(object):
def __init__(self, handle):
self.overlapped = _ffi.new('OVERLAPPED[1]')
self.handle = handle
self.readbuffer = None
self.pending = 0
self.completed = 0
self.writebuffer = None
self.overlapped[0].hEvent = \
_kernel32.CreateEventW(NULL, True, False, NULL)
def __del__(self):
# do this somehow else
xxx
err = _kernel32.GetLastError()
bytes = _ffi.new('DWORD[1]')
o = overlapped[0]
if overlapped[0].pending:
if _kernel32.CancelIoEx(o.handle, o.overlapped) & \
self.GetOverlappedResult(o.handle, o.overlapped, _ffi.addressof(bytes), True):
# The operation is no longer pending, nothing to do
pass
else:
raise RuntimeError('deleting an overlapped strucwith a pending operation not supported')
@property
def event(self):
return None
def GetOverlappedResult(self, wait):
transferred = _ffi.new('DWORD[1]', [0])
res = _kernel32.GetOverlappedResult(self.handle, self.overlapped, transferred, wait != 0)
if res:
err = ERROR_SUCCESS
else:
err = GetLastError()
if err in (ERROR_SUCCESS, ERROR_MORE_DATA, ERROR_OPERATION_ABORTED):
self.completed = 1
self.pending = 0
elif res == ERROR_IO_INCOMPLETE:
pass
else:
self.pending = 0
raise _WinError()
if self.completed and self.read_buffer:
if transferred != len(self.read_buffer):
raise _WinError()
return transferred[0], err
def getbuffer(self):
xxx
return None
def cancel(self):
xxx
return None
def ConnectNamedPipe(handle, overlapped=False):
if overlapped:
ov = Overlapped(handle)
else:
ov = Overlapped(None)
success = _kernel32.ConnectNamedPipe(handle, ov.overlapped)
if overlapped:
# Overlapped ConnectNamedPipe never returns a success code
assert success == 0
err = _kernel32.GetLastError()
if err == ERROR_IO_PENDING:
ov.pending = 1
elif err == ERROR_PIPE_CONNECTED:
_kernel32.SetEvent(ov.overlapped[0].hEvent)
else:
del ov
raise _WinError()
return ov
elif not success:
raise _WinError()
def GetCurrentProcess():
return _handle2int(_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 _handle2int(target[0])
def _Z(input):
if input is None:
return _ffi.NULL
if isinstance(input, str):
return input
raise TypeError("str or 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
# subprocess.Handle (int) objects
if startup_info.hStdInput:
si.hStdInput = _int2handle(startup_info.hStdInput)
if startup_info.hStdOutput:
si.hStdOutput = _int2handle(startup_info.hStdOutput)
if startup_info.hStdError:
si.hStdError = _int2handle(startup_info.hStdError)
pi = _ffi.new("PROCESS_INFORMATION *")
flags |= CREATE_UNICODE_ENVIRONMENT
if env is not None:
envbuf = ""
for k, v in env.items():
envbuf += "%s=%s\0" % (k, v)
envbuf += '\0'
else:
envbuf = _ffi.NULL
res = _kernel32.CreateProcessW(_Z(name), _Z(command_line), _ffi.NULL,
_ffi.NULL, inherit, flags, envbuf,
_Z(start_dir), si, pi)
if not res:
raise _WinError()
return (_handle2int(pi.hProcess),
_handle2int(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:
return _handle2int(res)
def CloseHandle(handle):
res = _kernel32.CloseHandle(_int2handle(handle))
if not res:
raise _WinError()
def GetModuleFileName(module):
buf = _ffi.new("wchar_t[]", _MAX_PATH)
res = _kernel32.GetModuleFileNameW(_int2handle(module), buf, _MAX_PATH)
if not res:
raise _WinError()
return _ffi.string(buf)
# #define macros from WinBase.h and elsewhere
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
WAIT_ABANDONED_0 = 0x80
WAIT_TIMEOUT = 0x102
CREATE_NEW_CONSOLE = 0x010
CREATE_NEW_PROCESS_GROUP = 0x200
CREATE_UNICODE_ENVIRONMENT = 0x400
STILL_ACTIVE = 259
_MAX_PATH = 260
ERROR_SUCCESS = 0
ERROR_NETNAME_DELETED = 64
ERROR_BROKEN_PIPE = 109
ERROR_MORE_DATA = 234
ERROR_PIPE_CONNECTED = 535
ERROR_OPERATION_ABORTED = 995
ERROR_IO_INCOMPLETE = 996
ERROR_IO_PENDING = 997
PIPE_ACCESS_INBOUND = 0x00000001
PIPE_ACCESS_OUTBOUND = 0x00000002
PIPE_ACCESS_DUPLEX = 0x00000003
PIPE_WAIT = 0x00000000
PIPE_NOWAIT = 0x00000001
PIPE_READMODE_BYTE = 0x00000000
PIPE_READMODE_MESSAGE = 0x00000002
PIPE_TYPE_BYTE = 0x00000000
PIPE_TYPE_MESSAGE = 0x00000004
PIPE_ACCEPT_REMOTE_CLIENTS = 0x00000000
PIPE_REJECT_REMOTE_CLIENTS = 0x00000008
GENERIC_READ = 0x80000000
GENERIC_WRITE = 0x40000000
GENERIC_EXECUTE= 0x20000000
GENERIC_ALL = 0x10000000
INVALID_HANDLE_VALUE = -1
FILE_FLAG_WRITE_THROUGH = 0x80000000
FILE_FLAG_OVERLAPPED = 0x40000000
FILE_FLAG_NO_BUFFERING = 0x20000000
FILE_FLAG_RANDOM_ACCESS = 0x10000000
FILE_FLAG_SEQUENTIAL_SCAN = 0x08000000
FILE_FLAG_DELETE_ON_CLOSE = 0x04000000
FILE_FLAG_BACKUP_SEMANTICS = 0x02000000
FILE_FLAG_POSIX_SEMANTICS = 0x01000000
FILE_FLAG_OPEN_REPARSE_POINT = 0x00200000
FILE_FLAG_OPEN_NO_RECALL = 0x00100000
FILE_FLAG_FIRST_PIPE_INSTANCE = 0x00080000
NMPWAIT_WAIT_FOREVER = 0xffffffff
NMPWAIT_NOWAIT = 0x00000001
NMPWAIT_USE_DEFAULT_WAIT = 0x00000000
CREATE_NEW = 1
CREATE_ALWAYS = 2
OPEN_EXISTING = 3
OPEN_ALWAYS = 4
TRUNCATE_EXISTING = 5
|