6 changed files with 16 additions and 196 deletions
@ -1,6 +1,6 @@ |
|||||
install: |
install: |
||||
- pip install --upgrade -r tests/requirements.txt |
- pip install --upgrade -r tests/requirements.txt |
||||
- pip install pypiwin32 |
- pip install pypiwin32 subprocessww |
||||
|
|
||||
build_script: |
build_script: |
||||
- pytest |
- pytest |
||||
|
@ -1,181 +0,0 @@ |
|||||
## Fixing python 2.7 windows unicode issue with ``subprocess.Popen``. |
|
||||
|
|
||||
## Copied from |
|
||||
## http://vaab.blog.kal.fr/2017/03/16/fixing-windows-python-2-7-unicode-issue-with-subprocesss-popen/ |
|
||||
## https://gist.github.com/vaab/2ad7051fc193167f15f85ef573e54eb9 |
|
||||
|
|
||||
## issue: https://bugs.python.org/issue19264 |
|
||||
import os |
|
||||
import time |
|
||||
import ctypes |
|
||||
import subprocess |
|
||||
import _subprocess |
|
||||
from ctypes import byref, windll, c_char_p, c_wchar_p, c_void_p, \ |
|
||||
Structure, sizeof, c_wchar, WinError |
|
||||
from ctypes.wintypes import BYTE, WORD, LPWSTR, BOOL, DWORD, LPVOID, \ |
|
||||
HANDLE |
|
||||
|
|
||||
|
|
||||
## |
|
||||
## Special counter because this function cannot |
|
||||
## be called within 2 seconds from each other! |
|
||||
## |
|
||||
_NEXT_PROCESS_START = 0.0 |
|
||||
_NEXT_PROCESS_DELAY = 2.0 |
|
||||
|
|
||||
|
|
||||
## |
|
||||
## Types |
|
||||
## |
|
||||
|
|
||||
CREATE_UNICODE_ENVIRONMENT = 0x00000400 |
|
||||
LPCTSTR = c_char_p |
|
||||
LPTSTR = c_wchar_p |
|
||||
LPSECURITY_ATTRIBUTES = c_void_p |
|
||||
LPBYTE = ctypes.POINTER(BYTE) |
|
||||
|
|
||||
class STARTUPINFOW(Structure): |
|
||||
_fields_ = [ |
|
||||
("cb", DWORD), ("lpReserved", LPWSTR), |
|
||||
("lpDesktop", LPWSTR), ("lpTitle", LPWSTR), |
|
||||
("dwX", DWORD), ("dwY", DWORD), |
|
||||
("dwXSize", DWORD), ("dwYSize", DWORD), |
|
||||
("dwXCountChars", DWORD), ("dwYCountChars", DWORD), |
|
||||
("dwFillAtrribute", DWORD), ("dwFlags", DWORD), |
|
||||
("wShowWindow", WORD), ("cbReserved2", WORD), |
|
||||
("lpReserved2", LPBYTE), ("hStdInput", HANDLE), |
|
||||
("hStdOutput", HANDLE), ("hStdError", HANDLE), |
|
||||
] |
|
||||
|
|
||||
LPSTARTUPINFOW = ctypes.POINTER(STARTUPINFOW) |
|
||||
|
|
||||
|
|
||||
class PROCESS_INFORMATION(Structure): |
|
||||
_fields_ = [ |
|
||||
("hProcess", HANDLE), ("hThread", HANDLE), |
|
||||
("dwProcessId", DWORD), ("dwThreadId", DWORD), |
|
||||
] |
|
||||
|
|
||||
LPPROCESS_INFORMATION = ctypes.POINTER(PROCESS_INFORMATION) |
|
||||
|
|
||||
|
|
||||
class DUMMY_HANDLE(ctypes.c_void_p): |
|
||||
|
|
||||
def __init__(self, *a, **kw): |
|
||||
super(DUMMY_HANDLE, self).__init__(*a, **kw) |
|
||||
self.closed = False |
|
||||
|
|
||||
def Close(self): |
|
||||
if not self.closed: |
|
||||
windll.kernel32.CloseHandle(self) |
|
||||
self.closed = True |
|
||||
|
|
||||
def __int__(self): |
|
||||
return self.value |
|
||||
|
|
||||
|
|
||||
CreateProcessW = windll.kernel32.CreateProcessW |
|
||||
CreateProcessW.argtypes = [ |
|
||||
LPCTSTR, LPTSTR, LPSECURITY_ATTRIBUTES, |
|
||||
LPSECURITY_ATTRIBUTES, BOOL, DWORD, LPVOID, LPCTSTR, |
|
||||
LPSTARTUPINFOW, LPPROCESS_INFORMATION, |
|
||||
] |
|
||||
CreateProcessW.restype = BOOL |
|
||||
|
|
||||
|
|
||||
## |
|
||||
## Patched functions/classes |
|
||||
## |
|
||||
|
|
||||
def CreateProcess(executable, args, _p_attr, _t_attr, |
|
||||
inherit_handles, creation_flags, env, cwd, |
|
||||
startup_info): |
|
||||
"""Create a process supporting unicode executable and args for win32 |
|
||||
|
|
||||
Python implementation of CreateProcess using CreateProcessW for Win32 |
|
||||
|
|
||||
""" |
|
||||
# Do we need to delay? |
|
||||
global _NEXT_PROCESS_START |
|
||||
diff_start = _NEXT_PROCESS_START - time.time() |
|
||||
if(diff_start > 0.0): |
|
||||
# Wait ourselves and make sure others also wait |
|
||||
_NEXT_PROCESS_START += _NEXT_PROCESS_DELAY |
|
||||
time.sleep(diff_start) |
|
||||
else: |
|
||||
_NEXT_PROCESS_START = time.time() + _NEXT_PROCESS_DELAY |
|
||||
|
|
||||
si = STARTUPINFOW( |
|
||||
dwFlags=startup_info.dwFlags, |
|
||||
wShowWindow=startup_info.wShowWindow, |
|
||||
cb=sizeof(STARTUPINFOW), |
|
||||
) |
|
||||
|
|
||||
# Only cast to ints when it's given |
|
||||
if startup_info.hStdInput: |
|
||||
si.hStdInput = int(startup_info.hStdInput) |
|
||||
if startup_info.hStdOutput: |
|
||||
si.hStdOutput = int(startup_info.hStdOutput) |
|
||||
if startup_info.hStdError: |
|
||||
si.hStdError = int(startup_info.hStdError) |
|
||||
|
|
||||
wenv = None |
|
||||
if env is not None: |
|
||||
## LPCWSTR seems to be c_wchar_p, so let's say CWSTR is c_wchar |
|
||||
env = (unicode("").join([ |
|
||||
unicode("%s=%s\0") % (k, v) |
|
||||
for k, v in env.items()])) + unicode("\0") |
|
||||
wenv = (c_wchar * len(env))() |
|
||||
wenv.value = env |
|
||||
|
|
||||
pi = PROCESS_INFORMATION() |
|
||||
creation_flags |= CREATE_UNICODE_ENVIRONMENT |
|
||||
|
|
||||
if CreateProcessW(executable, args, None, None, |
|
||||
inherit_handles, creation_flags, |
|
||||
wenv, cwd, byref(si), byref(pi)): |
|
||||
return (DUMMY_HANDLE(pi.hProcess), DUMMY_HANDLE(pi.hThread), |
|
||||
pi.dwProcessId, pi.dwThreadId) |
|
||||
raise WinError() |
|
||||
|
|
||||
|
|
||||
class Popen(subprocess.Popen): |
|
||||
"""This superseeds Popen and corrects a bug in cPython 2.7 implem""" |
|
||||
|
|
||||
def _execute_child(self, args, executable, preexec_fn, close_fds, |
|
||||
cwd, env, universal_newlines, |
|
||||
startupinfo, creationflags, shell, to_close, |
|
||||
p2cread, p2cwrite, |
|
||||
c2pread, c2pwrite, |
|
||||
errread, errwrite): |
|
||||
"""Code from part of _execute_child from Python 2.7 (9fbb65e) |
|
||||
|
|
||||
There are only 2 little changes concerning the construction of |
|
||||
the the final string in shell mode: we preempt the creation of |
|
||||
the command string when shell is True, because original function |
|
||||
will try to encode unicode args which we want to avoid to be able to |
|
||||
sending it as-is to ``CreateProcess``. |
|
||||
|
|
||||
""" |
|
||||
if not isinstance(args, subprocess.types.StringTypes): |
|
||||
args = subprocess.list2cmdline(args) |
|
||||
|
|
||||
if startupinfo is None: |
|
||||
startupinfo = subprocess.STARTUPINFO() |
|
||||
if shell: |
|
||||
startupinfo.dwFlags |= _subprocess.STARTF_USESHOWWINDOW |
|
||||
startupinfo.wShowWindow = _subprocess.SW_HIDE |
|
||||
comspec = os.environ.get("COMSPEC", unicode("cmd.exe")) |
|
||||
args = unicode('{} /c "{}"').format(comspec, args) |
|
||||
if (_subprocess.GetVersion() >= 0x80000000 or |
|
||||
os.path.basename(comspec).lower() == "command.com"): |
|
||||
w9xpopen = self._find_w9xpopen() |
|
||||
args = unicode('"%s" %s') % (w9xpopen, args) |
|
||||
creationflags |= _subprocess.CREATE_NEW_CONSOLE |
|
||||
|
|
||||
super(Popen, self)._execute_child(args, executable, |
|
||||
preexec_fn, close_fds, cwd, env, universal_newlines, |
|
||||
startupinfo, creationflags, False, to_close, p2cread, |
|
||||
p2cwrite, c2pread, c2pwrite, errread, errwrite) |
|
||||
|
|
||||
_subprocess.CreateProcess = CreateProcess |
|
Loading…
Reference in new issue