|
"""Windows-specific implementation of process utilities. |
|
|
|
This file is only meant to be imported by process.py, not by end-users. |
|
""" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import os |
|
import sys |
|
import ctypes |
|
import time |
|
|
|
from ctypes import c_int, POINTER |
|
from ctypes.wintypes import LPCWSTR, HLOCAL |
|
from subprocess import STDOUT, TimeoutExpired |
|
from threading import Thread |
|
|
|
|
|
from ._process_common import read_no_interrupt, process_handler, arg_split as py_arg_split |
|
from . import py3compat |
|
from .encoding import DEFAULT_ENCODING |
|
|
|
|
|
|
|
|
|
|
|
class AvoidUNCPath(object): |
|
"""A context manager to protect command execution from UNC paths. |
|
|
|
In the Win32 API, commands can't be invoked with the cwd being a UNC path. |
|
This context manager temporarily changes directory to the 'C:' drive on |
|
entering, and restores the original working directory on exit. |
|
|
|
The context manager returns the starting working directory *if* it made a |
|
change and None otherwise, so that users can apply the necessary adjustment |
|
to their system calls in the event of a change. |
|
|
|
Examples |
|
-------- |
|
:: |
|
cmd = 'dir' |
|
with AvoidUNCPath() as path: |
|
if path is not None: |
|
cmd = '"pushd %s &&"%s' % (path, cmd) |
|
os.system(cmd) |
|
""" |
|
def __enter__(self): |
|
self.path = os.getcwd() |
|
self.is_unc_path = self.path.startswith(r"\\") |
|
if self.is_unc_path: |
|
|
|
os.chdir("C:") |
|
return self.path |
|
else: |
|
|
|
|
|
return None |
|
|
|
def __exit__(self, exc_type, exc_value, traceback): |
|
if self.is_unc_path: |
|
os.chdir(self.path) |
|
|
|
|
|
def _system_body(p): |
|
"""Callback for _system.""" |
|
enc = DEFAULT_ENCODING |
|
|
|
def stdout_read(): |
|
for line in read_no_interrupt(p.stdout).splitlines(): |
|
line = line.decode(enc, 'replace') |
|
print(line, file=sys.stdout) |
|
|
|
def stderr_read(): |
|
for line in read_no_interrupt(p.stderr).splitlines(): |
|
line = line.decode(enc, 'replace') |
|
print(line, file=sys.stderr) |
|
|
|
Thread(target=stdout_read).start() |
|
Thread(target=stderr_read).start() |
|
|
|
|
|
|
|
|
|
while True: |
|
result = p.poll() |
|
if result is None: |
|
time.sleep(0.01) |
|
else: |
|
return result |
|
|
|
|
|
def system(cmd): |
|
"""Win32 version of os.system() that works with network shares. |
|
|
|
Note that this implementation returns None, as meant for use in IPython. |
|
|
|
Parameters |
|
---------- |
|
cmd : str or list |
|
A command to be executed in the system shell. |
|
|
|
Returns |
|
------- |
|
int : child process' exit code. |
|
""" |
|
|
|
|
|
|
|
|
|
|
|
with AvoidUNCPath() as path: |
|
if path is not None: |
|
cmd = '"pushd %s &&"%s' % (path, cmd) |
|
return process_handler(cmd, _system_body) |
|
|
|
def getoutput(cmd): |
|
"""Return standard output of executing cmd in a shell. |
|
|
|
Accepts the same arguments as os.system(). |
|
|
|
Parameters |
|
---------- |
|
cmd : str or list |
|
A command to be executed in the system shell. |
|
|
|
Returns |
|
------- |
|
stdout : str |
|
""" |
|
|
|
with AvoidUNCPath() as path: |
|
if path is not None: |
|
cmd = '"pushd %s &&"%s' % (path, cmd) |
|
out = process_handler(cmd, lambda p: p.communicate()[0], STDOUT) |
|
|
|
if out is None: |
|
out = b'' |
|
return py3compat.decode(out) |
|
|
|
try: |
|
CommandLineToArgvW = ctypes.windll.shell32.CommandLineToArgvW |
|
CommandLineToArgvW.arg_types = [LPCWSTR, POINTER(c_int)] |
|
CommandLineToArgvW.restype = POINTER(LPCWSTR) |
|
LocalFree = ctypes.windll.kernel32.LocalFree |
|
LocalFree.res_type = HLOCAL |
|
LocalFree.arg_types = [HLOCAL] |
|
|
|
def arg_split(commandline, posix=False, strict=True): |
|
"""Split a command line's arguments in a shell-like manner. |
|
|
|
This is a special version for windows that use a ctypes call to CommandLineToArgvW |
|
to do the argv splitting. The posix parameter is ignored. |
|
|
|
If strict=False, process_common.arg_split(...strict=False) is used instead. |
|
""" |
|
|
|
if commandline.strip() == "": |
|
return [] |
|
if not strict: |
|
|
|
return py_arg_split(commandline, posix=posix, strict=strict) |
|
argvn = c_int() |
|
result_pointer = CommandLineToArgvW(py3compat.cast_unicode(commandline.lstrip()), ctypes.byref(argvn)) |
|
result_array_type = LPCWSTR * argvn.value |
|
result = [arg for arg in result_array_type.from_address(ctypes.addressof(result_pointer.contents))] |
|
retval = LocalFree(result_pointer) |
|
return result |
|
except AttributeError: |
|
arg_split = py_arg_split |
|
|
|
def check_pid(pid): |
|
|
|
|
|
return bool(ctypes.windll.kernel32.OpenProcess(1,0,pid)) |
|
|