##// END OF EJS Templates
Tweak out the process controller class so it works interactively better...
Mark Wiebe -
Show More
@@ -1,183 +1,184 b''
1 """Windows-specific implementation of process utilities.
1 """Windows-specific implementation of process utilities.
2
2
3 This file is only meant to be imported by process.py, not by end-users.
3 This file is only meant to be imported by process.py, not by end-users.
4 """
4 """
5
5
6 #-----------------------------------------------------------------------------
6 #-----------------------------------------------------------------------------
7 # Copyright (C) 2010-2011 The IPython Development Team
7 # Copyright (C) 2010-2011 The IPython Development Team
8 #
8 #
9 # Distributed under the terms of the BSD License. The full license is in
9 # Distributed under the terms of the BSD License. The full license is in
10 # the file COPYING, distributed as part of this software.
10 # the file COPYING, distributed as part of this software.
11 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
12
12
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14 # Imports
14 # Imports
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16 from __future__ import print_function
16 from __future__ import print_function
17
17
18 # stdlib
18 # stdlib
19 import os
19 import os
20 import sys
20 import sys
21 import ctypes
21 import ctypes
22 import msvcrt
22
23
23 from ctypes import c_int, POINTER
24 from ctypes import c_int, POINTER
24 from ctypes.wintypes import LPCWSTR, HLOCAL
25 from ctypes.wintypes import LPCWSTR, HLOCAL
25 from subprocess import STDOUT
26 from subprocess import STDOUT
26
27
27 # our own imports
28 # our own imports
28 from ._process_common import read_no_interrupt, process_handler, arg_split as py_arg_split
29 from ._process_common import read_no_interrupt, process_handler, arg_split as py_arg_split
29 from . import py3compat
30 from . import py3compat
30 from . import text
31 from . import text
31
32
32 #-----------------------------------------------------------------------------
33 #-----------------------------------------------------------------------------
33 # Function definitions
34 # Function definitions
34 #-----------------------------------------------------------------------------
35 #-----------------------------------------------------------------------------
35
36
36 class AvoidUNCPath(object):
37 class AvoidUNCPath(object):
37 """A context manager to protect command execution from UNC paths.
38 """A context manager to protect command execution from UNC paths.
38
39
39 In the Win32 API, commands can't be invoked with the cwd being a UNC path.
40 In the Win32 API, commands can't be invoked with the cwd being a UNC path.
40 This context manager temporarily changes directory to the 'C:' drive on
41 This context manager temporarily changes directory to the 'C:' drive on
41 entering, and restores the original working directory on exit.
42 entering, and restores the original working directory on exit.
42
43
43 The context manager returns the starting working directory *if* it made a
44 The context manager returns the starting working directory *if* it made a
44 change and None otherwise, so that users can apply the necessary adjustment
45 change and None otherwise, so that users can apply the necessary adjustment
45 to their system calls in the event of a change.
46 to their system calls in the event of a change.
46
47
47 Example
48 Example
48 -------
49 -------
49 ::
50 ::
50 cmd = 'dir'
51 cmd = 'dir'
51 with AvoidUNCPath() as path:
52 with AvoidUNCPath() as path:
52 if path is not None:
53 if path is not None:
53 cmd = '"pushd %s &&"%s' % (path, cmd)
54 cmd = '"pushd %s &&"%s' % (path, cmd)
54 os.system(cmd)
55 os.system(cmd)
55 """
56 """
56 def __enter__(self):
57 def __enter__(self):
57 self.path = os.getcwdu()
58 self.path = os.getcwdu()
58 self.is_unc_path = self.path.startswith(r"\\")
59 self.is_unc_path = self.path.startswith(r"\\")
59 if self.is_unc_path:
60 if self.is_unc_path:
60 # change to c drive (as cmd.exe cannot handle UNC addresses)
61 # change to c drive (as cmd.exe cannot handle UNC addresses)
61 os.chdir("C:")
62 os.chdir("C:")
62 return self.path
63 return self.path
63 else:
64 else:
64 # We return None to signal that there was no change in the working
65 # We return None to signal that there was no change in the working
65 # directory
66 # directory
66 return None
67 return None
67
68
68 def __exit__(self, exc_type, exc_value, traceback):
69 def __exit__(self, exc_type, exc_value, traceback):
69 if self.is_unc_path:
70 if self.is_unc_path:
70 os.chdir(self.path)
71 os.chdir(self.path)
71
72
72
73
73 def _find_cmd(cmd):
74 def _find_cmd(cmd):
74 """Find the full path to a .bat or .exe using the win32api module."""
75 """Find the full path to a .bat or .exe using the win32api module."""
75 try:
76 try:
76 from win32api import SearchPath
77 from win32api import SearchPath
77 except ImportError:
78 except ImportError:
78 raise ImportError('you need to have pywin32 installed for this to work')
79 raise ImportError('you need to have pywin32 installed for this to work')
79 else:
80 else:
80 PATH = os.environ['PATH']
81 PATH = os.environ['PATH']
81 extensions = ['.exe', '.com', '.bat', '.py']
82 extensions = ['.exe', '.com', '.bat', '.py']
82 path = None
83 path = None
83 for ext in extensions:
84 for ext in extensions:
84 try:
85 try:
85 path = SearchPath(PATH, cmd + ext)[0]
86 path = SearchPath(PATH, cmd + ext)[0]
86 except:
87 except:
87 pass
88 pass
88 if path is None:
89 if path is None:
89 raise OSError("command %r not found" % cmd)
90 raise OSError("command %r not found" % cmd)
90 else:
91 else:
91 return path
92 return path
92
93
93
94
94 def _system_body(p):
95 def _system_body(p):
95 """Callback for _system."""
96 """Callback for _system."""
96 enc = text.getdefaultencoding()
97 enc = text.getdefaultencoding()
97 for line in read_no_interrupt(p.stdout).splitlines():
98 for line in read_no_interrupt(p.stdout).splitlines():
98 line = line.decode(enc, 'replace')
99 line = line.decode(enc, 'replace')
99 print(line, file=sys.stdout)
100 print(line, file=sys.stdout)
100 for line in read_no_interrupt(p.stderr).splitlines():
101 for line in read_no_interrupt(p.stderr).splitlines():
101 line = line.decode(enc, 'replace')
102 line = line.decode(enc, 'replace')
102 print(line, file=sys.stderr)
103 print(line, file=sys.stderr)
103
104
104 # Wait to finish for returncode
105 # Wait to finish for returncode
105 return p.wait()
106 return p.wait()
106
107
107
108
108 def system(cmd):
109 def system(cmd):
109 """Win32 version of os.system() that works with network shares.
110 """Win32 version of os.system() that works with network shares.
110
111
111 Note that this implementation returns None, as meant for use in IPython.
112 Note that this implementation returns None, as meant for use in IPython.
112
113
113 Parameters
114 Parameters
114 ----------
115 ----------
115 cmd : str
116 cmd : str
116 A command to be executed in the system shell.
117 A command to be executed in the system shell.
117
118
118 Returns
119 Returns
119 -------
120 -------
120 None : we explicitly do NOT return the subprocess status code, as this
121 None : we explicitly do NOT return the subprocess status code, as this
121 utility is meant to be used extensively in IPython, where any return value
122 utility is meant to be used extensively in IPython, where any return value
122 would trigger :func:`sys.displayhook` calls.
123 would trigger :func:`sys.displayhook` calls.
123 """
124 """
124 with AvoidUNCPath() as path:
125 # The controller provides interactivity with both
125 if path is not None:
126 # stdin and stdout
126 cmd = '"pushd %s &&"%s' % (path, cmd)
127 import _process_win32_controller
127 return process_handler(cmd, _system_body)
128 _process_win32_controller.system(cmd)
128
129
129
130
130 def getoutput(cmd):
131 def getoutput(cmd):
131 """Return standard output of executing cmd in a shell.
132 """Return standard output of executing cmd in a shell.
132
133
133 Accepts the same arguments as os.system().
134 Accepts the same arguments as os.system().
134
135
135 Parameters
136 Parameters
136 ----------
137 ----------
137 cmd : str
138 cmd : str
138 A command to be executed in the system shell.
139 A command to be executed in the system shell.
139
140
140 Returns
141 Returns
141 -------
142 -------
142 stdout : str
143 stdout : str
143 """
144 """
144
145
145 with AvoidUNCPath() as path:
146 with AvoidUNCPath() as path:
146 if path is not None:
147 if path is not None:
147 cmd = '"pushd %s &&"%s' % (path, cmd)
148 cmd = '"pushd %s &&"%s' % (path, cmd)
148 out = process_handler(cmd, lambda p: p.communicate()[0], STDOUT)
149 out = process_handler(cmd, lambda p: p.communicate()[0], STDOUT)
149
150
150 if out is None:
151 if out is None:
151 out = ''
152 out = ''
152 return out
153 return out
153
154
154 try:
155 try:
155 CommandLineToArgvW = ctypes.windll.shell32.CommandLineToArgvW
156 CommandLineToArgvW = ctypes.windll.shell32.CommandLineToArgvW
156 CommandLineToArgvW.arg_types = [LPCWSTR, POINTER(c_int)]
157 CommandLineToArgvW.arg_types = [LPCWSTR, POINTER(c_int)]
157 CommandLineToArgvW.res_types = [POINTER(LPCWSTR)]
158 CommandLineToArgvW.res_types = [POINTER(LPCWSTR)]
158 LocalFree = ctypes.windll.kernel32.LocalFree
159 LocalFree = ctypes.windll.kernel32.LocalFree
159 LocalFree.res_type = HLOCAL
160 LocalFree.res_type = HLOCAL
160 LocalFree.arg_types = [HLOCAL]
161 LocalFree.arg_types = [HLOCAL]
161
162
162 def arg_split(commandline, posix=False, strict=True):
163 def arg_split(commandline, posix=False, strict=True):
163 """Split a command line's arguments in a shell-like manner.
164 """Split a command line's arguments in a shell-like manner.
164
165
165 This is a special version for windows that use a ctypes call to CommandLineToArgvW
166 This is a special version for windows that use a ctypes call to CommandLineToArgvW
166 to do the argv splitting. The posix paramter is ignored.
167 to do the argv splitting. The posix paramter is ignored.
167
168
168 If strict=False, process_common.arg_split(...strict=False) is used instead.
169 If strict=False, process_common.arg_split(...strict=False) is used instead.
169 """
170 """
170 #CommandLineToArgvW returns path to executable if called with empty string.
171 #CommandLineToArgvW returns path to executable if called with empty string.
171 if commandline.strip() == "":
172 if commandline.strip() == "":
172 return []
173 return []
173 if not strict:
174 if not strict:
174 # not really a cl-arg, fallback on _process_common
175 # not really a cl-arg, fallback on _process_common
175 return py_arg_split(commandline, posix=posix, strict=strict)
176 return py_arg_split(commandline, posix=posix, strict=strict)
176 argvn = c_int()
177 argvn = c_int()
177 result_pointer = CommandLineToArgvW(py3compat.cast_unicode(commandline.lstrip()), ctypes.byref(argvn))
178 result_pointer = CommandLineToArgvW(py3compat.cast_unicode(commandline.lstrip()), ctypes.byref(argvn))
178 result_array_type = LPCWSTR * argvn.value
179 result_array_type = LPCWSTR * argvn.value
179 result = [arg for arg in result_array_type.from_address(result_pointer)]
180 result = [arg for arg in result_array_type.from_address(result_pointer)]
180 retval = LocalFree(result_pointer)
181 retval = LocalFree(result_pointer)
181 return result
182 return result
182 except AttributeError:
183 except AttributeError:
183 arg_split = py_arg_split
184 arg_split = py_arg_split
@@ -1,502 +1,574 b''
1 """Windows-specific implementation of process utilities with direct WinAPI.
1 """Windows-specific implementation of process utilities with direct WinAPI.
2
2
3 This file is meant to be used by process.py
3 This file is meant to be used by process.py
4 """
4 """
5
5
6 #-----------------------------------------------------------------------------
6 #-----------------------------------------------------------------------------
7 # Copyright (C) 2010-2011 The IPython Development Team
7 # Copyright (C) 2010-2011 The IPython Development Team
8 #
8 #
9 # Distributed under the terms of the BSD License. The full license is in
9 # Distributed under the terms of the BSD License. The full license is in
10 # the file COPYING, distributed as part of this software.
10 # the file COPYING, distributed as part of this software.
11 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
12
12
13 from __future__ import print_function
13 from __future__ import print_function
14
14
15 # stdlib
15 # stdlib
16 import os, sys, time, threading
16 import os, sys, time, threading
17 import ctypes, msvcrt
17 import ctypes, msvcrt
18
18
19 # Win32 API types needed for the API calls
19 # Win32 API types needed for the API calls
20 from ctypes import POINTER
20 from ctypes import POINTER
21 from ctypes.wintypes import HANDLE, HLOCAL, LPVOID, WORD, DWORD, BOOL, \
21 from ctypes.wintypes import HANDLE, HLOCAL, LPVOID, WORD, DWORD, BOOL, \
22 ULONG, LPCWSTR
22 ULONG, LPCWSTR
23 LPDWORD = POINTER(DWORD)
23 LPDWORD = POINTER(DWORD)
24 LPHANDLE = POINTER(HANDLE)
24 LPHANDLE = POINTER(HANDLE)
25 ULONG_PTR = POINTER(ULONG)
25 ULONG_PTR = POINTER(ULONG)
26 class SECURITY_ATTRIBUTES(ctypes.Structure):
26 class SECURITY_ATTRIBUTES(ctypes.Structure):
27 _fields_ = [("nLength", DWORD),
27 _fields_ = [("nLength", DWORD),
28 ("lpSecurityDescriptor", LPVOID),
28 ("lpSecurityDescriptor", LPVOID),
29 ("bInheritHandle", BOOL)]
29 ("bInheritHandle", BOOL)]
30 LPSECURITY_ATTRIBUTES = POINTER(SECURITY_ATTRIBUTES)
30 LPSECURITY_ATTRIBUTES = POINTER(SECURITY_ATTRIBUTES)
31 class STARTUPINFO(ctypes.Structure):
31 class STARTUPINFO(ctypes.Structure):
32 _fields_ = [("cb", DWORD),
32 _fields_ = [("cb", DWORD),
33 ("lpReserved", LPCWSTR),
33 ("lpReserved", LPCWSTR),
34 ("lpDesktop", LPCWSTR),
34 ("lpDesktop", LPCWSTR),
35 ("lpTitle", LPCWSTR),
35 ("lpTitle", LPCWSTR),
36 ("dwX", DWORD),
36 ("dwX", DWORD),
37 ("dwY", DWORD),
37 ("dwY", DWORD),
38 ("dwXSize", DWORD),
38 ("dwXSize", DWORD),
39 ("dwYSize", DWORD),
39 ("dwYSize", DWORD),
40 ("dwXCountChars", DWORD),
40 ("dwXCountChars", DWORD),
41 ("dwYCountChars", DWORD),
41 ("dwYCountChars", DWORD),
42 ("dwFillAttribute", DWORD),
42 ("dwFillAttribute", DWORD),
43 ("dwFlags", DWORD),
43 ("dwFlags", DWORD),
44 ("wShowWindow", WORD),
44 ("wShowWindow", WORD),
45 ("cbReserved2", WORD),
45 ("cbReserved2", WORD),
46 ("lpReserved2", LPVOID),
46 ("lpReserved2", LPVOID),
47 ("hStdInput", HANDLE),
47 ("hStdInput", HANDLE),
48 ("hStdOutput", HANDLE),
48 ("hStdOutput", HANDLE),
49 ("hStdError", HANDLE)]
49 ("hStdError", HANDLE)]
50 LPSTARTUPINFO = POINTER(STARTUPINFO)
50 LPSTARTUPINFO = POINTER(STARTUPINFO)
51 class PROCESS_INFORMATION(ctypes.Structure):
51 class PROCESS_INFORMATION(ctypes.Structure):
52 _fields_ = [("hProcess", HANDLE),
52 _fields_ = [("hProcess", HANDLE),
53 ("hThread", HANDLE),
53 ("hThread", HANDLE),
54 ("dwProcessId", DWORD),
54 ("dwProcessId", DWORD),
55 ("dwThreadId", DWORD)]
55 ("dwThreadId", DWORD)]
56 LPPROCESS_INFORMATION = POINTER(PROCESS_INFORMATION)
56 LPPROCESS_INFORMATION = POINTER(PROCESS_INFORMATION)
57
57
58 # Win32 API constants needed
58 # Win32 API constants needed
59 ERROR_HANDLE_EOF = 38
59 ERROR_HANDLE_EOF = 38
60 ERROR_BROKEN_PIPE = 109
60 ERROR_BROKEN_PIPE = 109
61 ERROR_NO_DATA = 232
61 HANDLE_FLAG_INHERIT = 0x0001
62 HANDLE_FLAG_INHERIT = 0x0001
62 STARTF_USESTDHANDLES = 0x0100
63 STARTF_USESTDHANDLES = 0x0100
63 CREATE_SUSPENDED = 0x0004
64 CREATE_SUSPENDED = 0x0004
64 CREATE_NEW_CONSOLE = 0x0010
65 CREATE_NEW_CONSOLE = 0x0010
66 CREATE_NO_WINDOW = 0x08000000
65 STILL_ACTIVE = 259
67 STILL_ACTIVE = 259
66 WAIT_TIMEOUT = 0x0102
68 WAIT_TIMEOUT = 0x0102
67 WAIT_FAILED = 0xFFFFFFFF
69 WAIT_FAILED = 0xFFFFFFFF
68 INFINITE = 0xFFFFFFFF
70 INFINITE = 0xFFFFFFFF
69 DUPLICATE_SAME_ACCESS = 0x00000002
71 DUPLICATE_SAME_ACCESS = 0x00000002
72 ENABLE_ECHO_INPUT = 0x0004
73 ENABLE_LINE_INPUT = 0x0002
74 ENABLE_PROCESSED_INPUT = 0x0001
70
75
71 # Win32 API functions needed
76 # Win32 API functions needed
72 GetLastError = ctypes.windll.kernel32.GetLastError
77 GetLastError = ctypes.windll.kernel32.GetLastError
73 GetLastError.argtypes = []
78 GetLastError.argtypes = []
74 GetLastError.restype = DWORD
79 GetLastError.restype = DWORD
75
80
76 CreateFile = ctypes.windll.kernel32.CreateFileW
81 CreateFile = ctypes.windll.kernel32.CreateFileW
77 CreateFile.argtypes = [LPCWSTR, DWORD, DWORD, LPVOID, DWORD, DWORD, HANDLE]
82 CreateFile.argtypes = [LPCWSTR, DWORD, DWORD, LPVOID, DWORD, DWORD, HANDLE]
78 CreateFile.restype = HANDLE
83 CreateFile.restype = HANDLE
79
84
80 CreatePipe = ctypes.windll.kernel32.CreatePipe
85 CreatePipe = ctypes.windll.kernel32.CreatePipe
81 CreatePipe.argtypes = [POINTER(HANDLE), POINTER(HANDLE),
86 CreatePipe.argtypes = [POINTER(HANDLE), POINTER(HANDLE),
82 LPSECURITY_ATTRIBUTES, DWORD]
87 LPSECURITY_ATTRIBUTES, DWORD]
83 CreatePipe.restype = BOOL
88 CreatePipe.restype = BOOL
84
89
85 CreateProcess = ctypes.windll.kernel32.CreateProcessW
90 CreateProcess = ctypes.windll.kernel32.CreateProcessW
86 CreateProcess.argtypes = [LPCWSTR, LPCWSTR, LPSECURITY_ATTRIBUTES,
91 CreateProcess.argtypes = [LPCWSTR, LPCWSTR, LPSECURITY_ATTRIBUTES,
87 LPSECURITY_ATTRIBUTES, BOOL, DWORD, LPVOID, LPCWSTR, LPSTARTUPINFO,
92 LPSECURITY_ATTRIBUTES, BOOL, DWORD, LPVOID, LPCWSTR, LPSTARTUPINFO,
88 LPPROCESS_INFORMATION]
93 LPPROCESS_INFORMATION]
89 CreateProcess.restype = BOOL
94 CreateProcess.restype = BOOL
90
95
91 GetExitCodeProcess = ctypes.windll.kernel32.GetExitCodeProcess
96 GetExitCodeProcess = ctypes.windll.kernel32.GetExitCodeProcess
92 GetExitCodeProcess.argtypes = [HANDLE, LPDWORD]
97 GetExitCodeProcess.argtypes = [HANDLE, LPDWORD]
93 GetExitCodeProcess.restype = BOOL
98 GetExitCodeProcess.restype = BOOL
94
99
95 GetCurrentProcess = ctypes.windll.kernel32.GetCurrentProcess
100 GetCurrentProcess = ctypes.windll.kernel32.GetCurrentProcess
96 GetCurrentProcess.argtypes = []
101 GetCurrentProcess.argtypes = []
97 GetCurrentProcess.restype = HANDLE
102 GetCurrentProcess.restype = HANDLE
98
103
99 ResumeThread = ctypes.windll.kernel32.ResumeThread
104 ResumeThread = ctypes.windll.kernel32.ResumeThread
100 ResumeThread.argtypes = [HANDLE]
105 ResumeThread.argtypes = [HANDLE]
101 ResumeThread.restype = DWORD
106 ResumeThread.restype = DWORD
102
107
103 ReadFile = ctypes.windll.kernel32.ReadFile
108 ReadFile = ctypes.windll.kernel32.ReadFile
104 ReadFile.argtypes = [HANDLE, LPVOID, DWORD, LPDWORD, LPVOID]
109 ReadFile.argtypes = [HANDLE, LPVOID, DWORD, LPDWORD, LPVOID]
105 ReadFile.restype = BOOL
110 ReadFile.restype = BOOL
106
111
107 WriteFile = ctypes.windll.kernel32.WriteFile
112 WriteFile = ctypes.windll.kernel32.WriteFile
108 WriteFile.argtypes = [HANDLE, LPVOID, DWORD, LPDWORD, LPVOID]
113 WriteFile.argtypes = [HANDLE, LPVOID, DWORD, LPDWORD, LPVOID]
109 WriteFile.restype = BOOL
114 WriteFile.restype = BOOL
110
115
116 GetConsoleMode = ctypes.windll.kernel32.GetConsoleMode
117 GetConsoleMode.argtypes = [HANDLE, LPDWORD]
118 GetConsoleMode.restype = BOOL
119
120 SetConsoleMode = ctypes.windll.kernel32.SetConsoleMode
121 SetConsoleMode.argtypes = [HANDLE, DWORD]
122 SetConsoleMode.restype = BOOL
123
124 FlushConsoleInputBuffer = ctypes.windll.kernel32.FlushConsoleInputBuffer
125 FlushConsoleInputBuffer.argtypes = [HANDLE]
126 FlushConsoleInputBuffer.restype = BOOL
127
111 WaitForSingleObject = ctypes.windll.kernel32.WaitForSingleObject
128 WaitForSingleObject = ctypes.windll.kernel32.WaitForSingleObject
112 WaitForSingleObject.argtypes = [HANDLE, DWORD]
129 WaitForSingleObject.argtypes = [HANDLE, DWORD]
113 WaitForSingleObject.restype = DWORD
130 WaitForSingleObject.restype = DWORD
114
131
115 DuplicateHandle = ctypes.windll.kernel32.DuplicateHandle
132 DuplicateHandle = ctypes.windll.kernel32.DuplicateHandle
116 DuplicateHandle.argtypes = [HANDLE, HANDLE, HANDLE, LPHANDLE,
133 DuplicateHandle.argtypes = [HANDLE, HANDLE, HANDLE, LPHANDLE,
117 DWORD, BOOL, DWORD]
134 DWORD, BOOL, DWORD]
118 DuplicateHandle.restype = BOOL
135 DuplicateHandle.restype = BOOL
119
136
120 SetHandleInformation = ctypes.windll.kernel32.SetHandleInformation
137 SetHandleInformation = ctypes.windll.kernel32.SetHandleInformation
121 SetHandleInformation.argtypes = [HANDLE, DWORD, DWORD]
138 SetHandleInformation.argtypes = [HANDLE, DWORD, DWORD]
122 SetHandleInformation.restype = BOOL
139 SetHandleInformation.restype = BOOL
123
140
124 CloseHandle = ctypes.windll.kernel32.CloseHandle
141 CloseHandle = ctypes.windll.kernel32.CloseHandle
125 CloseHandle.argtypes = [HANDLE]
142 CloseHandle.argtypes = [HANDLE]
126 CloseHandle.restype = BOOL
143 CloseHandle.restype = BOOL
127
144
128 CommandLineToArgvW = ctypes.windll.shell32.CommandLineToArgvW
145 CommandLineToArgvW = ctypes.windll.shell32.CommandLineToArgvW
129 CommandLineToArgvW.argtypes = [LPCWSTR, POINTER(ctypes.c_int)]
146 CommandLineToArgvW.argtypes = [LPCWSTR, POINTER(ctypes.c_int)]
130 CommandLineToArgvW.restype = POINTER(LPCWSTR)
147 CommandLineToArgvW.restype = POINTER(LPCWSTR)
131
148
132 LocalFree = ctypes.windll.kernel32.LocalFree
149 LocalFree = ctypes.windll.kernel32.LocalFree
133 LocalFree.argtypes = [HLOCAL]
150 LocalFree.argtypes = [HLOCAL]
134 LocalFree.restype = HLOCAL
151 LocalFree.restype = HLOCAL
135
152
136 class AvoidUNCPath(object):
153 class AvoidUNCPath(object):
137 """A context manager to protect command execution from UNC paths.
154 """A context manager to protect command execution from UNC paths.
138
155
139 In the Win32 API, commands can't be invoked with the cwd being a UNC path.
156 In the Win32 API, commands can't be invoked with the cwd being a UNC path.
140 This context manager temporarily changes directory to the 'C:' drive on
157 This context manager temporarily changes directory to the 'C:' drive on
141 entering, and restores the original working directory on exit.
158 entering, and restores the original working directory on exit.
142
159
143 The context manager returns the starting working directory *if* it made a
160 The context manager returns the starting working directory *if* it made a
144 change and None otherwise, so that users can apply the necessary adjustment
161 change and None otherwise, so that users can apply the necessary adjustment
145 to their system calls in the event of a change.
162 to their system calls in the event of a change.
146
163
147 Example
164 Example
148 -------
165 -------
149 ::
166 ::
150 cmd = 'dir'
167 cmd = 'dir'
151 with AvoidUNCPath() as path:
168 with AvoidUNCPath() as path:
152 if path is not None:
169 if path is not None:
153 cmd = '"pushd %s &&"%s' % (path, cmd)
170 cmd = '"pushd %s &&"%s' % (path, cmd)
154 os.system(cmd)
171 os.system(cmd)
155 """
172 """
156 def __enter__(self):
173 def __enter__(self):
157 self.path = os.getcwdu()
174 self.path = os.getcwdu()
158 self.is_unc_path = self.path.startswith(r"\\")
175 self.is_unc_path = self.path.startswith(r"\\")
159 if self.is_unc_path:
176 if self.is_unc_path:
160 # change to c drive (as cmd.exe cannot handle UNC addresses)
177 # change to c drive (as cmd.exe cannot handle UNC addresses)
161 os.chdir("C:")
178 os.chdir("C:")
162 return self.path
179 return self.path
163 else:
180 else:
164 # We return None to signal that there was no change in the working
181 # We return None to signal that there was no change in the working
165 # directory
182 # directory
166 return None
183 return None
167
184
168 def __exit__(self, exc_type, exc_value, traceback):
185 def __exit__(self, exc_type, exc_value, traceback):
169 if self.is_unc_path:
186 if self.is_unc_path:
170 os.chdir(self.path)
187 os.chdir(self.path)
171
188
172
189
173 class Win32ShellCommandController(object):
190 class Win32ShellCommandController(object):
174 """Runs a shell command in a 'with' context.
191 """Runs a shell command in a 'with' context.
175
192
176 This implementation is Win32-specific.
193 This implementation is Win32-specific.
177
194
178 Example:
195 Example:
179 # Runs the command interactively with default console stdin/stdout
196 # Runs the command interactively with default console stdin/stdout
180 with ShellCommandController('python -i') as scc:
197 with ShellCommandController('python -i') as scc:
181 scc.run()
198 scc.run()
182
199
183 # Runs the command using the provided functions for stdin/stdout
200 # Runs the command using the provided functions for stdin/stdout
184 def my_stdout_func(s):
201 def my_stdout_func(s):
185 # print or save the string 's'
202 # print or save the string 's'
186 write_to_stdout(s)
203 write_to_stdout(s)
187 def my_stdin_func():
204 def my_stdin_func():
188 # If input is available, return it as a string.
205 # If input is available, return it as a string.
189 if input_available():
206 if input_available():
190 return get_input()
207 return get_input()
191 # If no input available, return None after a short delay to
208 # If no input available, return None after a short delay to
192 # keep from blocking.
209 # keep from blocking.
193 else:
210 else:
194 time.sleep(0.01)
211 time.sleep(0.01)
195 return None
212 return None
196
213
197 with ShellCommandController('python -i') as scc:
214 with ShellCommandController('python -i') as scc:
198 scc.run(my_stdout_func, my_stdin_func)
215 scc.run(my_stdout_func, my_stdin_func)
199 """
216 """
200
217
201 def __init__(self, cmd, mergeout = True):
218 def __init__(self, cmd, mergeout = True):
202 """Initializes the shell command controller.
219 """Initializes the shell command controller.
203
220
204 The cmd is the program to execute, and mergeout is
221 The cmd is the program to execute, and mergeout is
205 whether to blend stdout and stderr into one output
222 whether to blend stdout and stderr into one output
206 in stdout. Merging them together in this fashion more
223 in stdout. Merging them together in this fashion more
207 reliably keeps stdout and stderr in the correct order
224 reliably keeps stdout and stderr in the correct order
208 especially for interactive shell usage.
225 especially for interactive shell usage.
209 """
226 """
210 self.cmd = cmd
227 self.cmd = cmd
211 self.mergeout = mergeout
228 self.mergeout = mergeout
212
229
213 def __enter__(self):
230 def __enter__(self):
214 cmd = self.cmd
231 cmd = self.cmd
215 mergeout = self.mergeout
232 mergeout = self.mergeout
216
233
217 self.hstdout, self.hstdin, self.hstderr = None, None, None
234 self.hstdout, self.hstdin, self.hstderr = None, None, None
218 self.piProcInfo = None
235 self.piProcInfo = None
219 try:
236 try:
220 p_hstdout, c_hstdout, p_hstderr, \
237 p_hstdout, c_hstdout, p_hstderr, \
221 c_hstderr, p_hstdin, c_hstdin = [None]*6
238 c_hstderr, p_hstdin, c_hstdin = [None]*6
222
239
223 # SECURITY_ATTRIBUTES with inherit handle set to True
240 # SECURITY_ATTRIBUTES with inherit handle set to True
224 saAttr = SECURITY_ATTRIBUTES()
241 saAttr = SECURITY_ATTRIBUTES()
225 saAttr.nLength = ctypes.sizeof(saAttr)
242 saAttr.nLength = ctypes.sizeof(saAttr)
226 saAttr.bInheritHandle = True
243 saAttr.bInheritHandle = True
227 saAttr.lpSecurityDescriptor = None
244 saAttr.lpSecurityDescriptor = None
228
245
229 def create_pipe(uninherit):
246 def create_pipe(uninherit):
230 """Creates a Windows pipe, which consists of two handles.
247 """Creates a Windows pipe, which consists of two handles.
231
248
232 The 'uninherit' parameter controls which handle is not
249 The 'uninherit' parameter controls which handle is not
233 inherited by the child process.
250 inherited by the child process.
234 """
251 """
235 handles = HANDLE(), HANDLE()
252 handles = HANDLE(), HANDLE()
236 if not CreatePipe(ctypes.byref(handles[0]),
253 if not CreatePipe(ctypes.byref(handles[0]),
237 ctypes.byref(handles[1]), ctypes.byref(saAttr), 0):
254 ctypes.byref(handles[1]), ctypes.byref(saAttr), 0):
238 raise ctypes.WinError()
255 raise ctypes.WinError()
239 if not SetHandleInformation(handles[uninherit],
256 if not SetHandleInformation(handles[uninherit],
240 HANDLE_FLAG_INHERIT, 0):
257 HANDLE_FLAG_INHERIT, 0):
241 raise ctypes.WinError()
258 raise ctypes.WinError()
242 return handles[0].value, handles[1].value
259 return handles[0].value, handles[1].value
243
260
244 p_hstdout, c_hstdout = create_pipe(uninherit=0)
261 p_hstdout, c_hstdout = create_pipe(uninherit=0)
245 # 'mergeout' signals that stdout and stderr should be merged.
262 # 'mergeout' signals that stdout and stderr should be merged.
246 # We do that by using one pipe for both of them.
263 # We do that by using one pipe for both of them.
247 if mergeout:
264 if mergeout:
248 c_hstderr = HANDLE()
265 c_hstderr = HANDLE()
249 if not DuplicateHandle(GetCurrentProcess(), c_hstdout,
266 if not DuplicateHandle(GetCurrentProcess(), c_hstdout,
250 GetCurrentProcess(), ctypes.byref(c_hstderr),
267 GetCurrentProcess(), ctypes.byref(c_hstderr),
251 0, True, DUPLICATE_SAME_ACCESS):
268 0, True, DUPLICATE_SAME_ACCESS):
252 raise ctypes.WinError()
269 raise ctypes.WinError()
253 else:
270 else:
254 p_hstderr, c_hstderr = create_pipe(uninherit=0)
271 p_hstderr, c_hstderr = create_pipe(uninherit=0)
255 c_hstdin, p_hstdin = create_pipe(uninherit=1)
272 c_hstdin, p_hstdin = create_pipe(uninherit=1)
256
273
257 # Create the process object
274 # Create the process object
258 piProcInfo = PROCESS_INFORMATION()
275 piProcInfo = PROCESS_INFORMATION()
259 siStartInfo = STARTUPINFO()
276 siStartInfo = STARTUPINFO()
260 siStartInfo.cb = ctypes.sizeof(siStartInfo)
277 siStartInfo.cb = ctypes.sizeof(siStartInfo)
261 siStartInfo.hStdInput = c_hstdin
278 siStartInfo.hStdInput = c_hstdin
262 siStartInfo.hStdOutput = c_hstdout
279 siStartInfo.hStdOutput = c_hstdout
263 siStartInfo.hStdError = c_hstderr
280 siStartInfo.hStdError = c_hstderr
264 siStartInfo.dwFlags = STARTF_USESTDHANDLES
281 siStartInfo.dwFlags = STARTF_USESTDHANDLES
265 dwCreationFlags = CREATE_SUSPENDED # | CREATE_NEW_CONSOLE
282 dwCreationFlags = CREATE_SUSPENDED | CREATE_NO_WINDOW # | CREATE_NEW_CONSOLE
266
283
267 if not CreateProcess(None,
284 if not CreateProcess(None,
268 u"cmd.exe /c " + cmd,
285 u"cmd.exe /c " + cmd,
269 None, None, True, dwCreationFlags,
286 None, None, True, dwCreationFlags,
270 None, None, ctypes.byref(siStartInfo),
287 None, None, ctypes.byref(siStartInfo),
271 ctypes.byref(piProcInfo)):
288 ctypes.byref(piProcInfo)):
272 raise ctypes.WinError()
289 raise ctypes.WinError()
273
290
274 # Close this process's versions of the child handles
291 # Close this process's versions of the child handles
275 CloseHandle(c_hstdin)
292 CloseHandle(c_hstdin)
276 c_hstdin = None
293 c_hstdin = None
277 CloseHandle(c_hstdout)
294 CloseHandle(c_hstdout)
278 c_hstdout = None
295 c_hstdout = None
279 if c_hstderr != None:
296 if c_hstderr != None:
280 CloseHandle(c_hstderr)
297 CloseHandle(c_hstderr)
281 c_hstderr = None
298 c_hstderr = None
282
299
283 # Transfer ownership of the parent handles to the object
300 # Transfer ownership of the parent handles to the object
284 self.hstdin = p_hstdin
301 self.hstdin = p_hstdin
285 p_hstdin = None
302 p_hstdin = None
286 self.hstdout = p_hstdout
303 self.hstdout = p_hstdout
287 p_hstdout = None
304 p_hstdout = None
288 if not mergeout:
305 if not mergeout:
289 self.hstderr = p_hstderr
306 self.hstderr = p_hstderr
290 p_hstderr = None
307 p_hstderr = None
291 self.piProcInfo = piProcInfo
308 self.piProcInfo = piProcInfo
292
309
293 finally:
310 finally:
294 if p_hstdin:
311 if p_hstdin:
295 CloseHandle(p_hstdin)
312 CloseHandle(p_hstdin)
296 if c_hstdin:
313 if c_hstdin:
297 CloseHandle(c_hstdin)
314 CloseHandle(c_hstdin)
298 if p_hstdout:
315 if p_hstdout:
299 CloseHandle(p_hstdout)
316 CloseHandle(p_hstdout)
300 if c_hstdout:
317 if c_hstdout:
301 CloseHandle(c_hstdout)
318 CloseHandle(c_hstdout)
302 if p_hstderr:
319 if p_hstderr:
303 CloseHandle(p_hstderr)
320 CloseHandle(p_hstderr)
304 if c_hstderr:
321 if c_hstderr:
305 CloseHandle(c_hstderr)
322 CloseHandle(c_hstderr)
306
323
307 return self
324 return self
308
325
309 def _stdin_thread(self, handle, hprocess, func, stdout_func):
326 def _stdin_thread(self, handle, hprocess, func, stdout_func):
310 # TODO: Use WaitForInputIdle to avoid calling func() until
311 # an input is actually requested.
312 exitCode = DWORD()
327 exitCode = DWORD()
313 bytesWritten = DWORD(0)
328 bytesWritten = DWORD(0)
314 while True:
329 while True:
315 #print("stdin thread loop start")
330 #print("stdin thread loop start")
316 # Get the input string (may be bytes or unicode)
331 # Get the input string (may be bytes or unicode)
317 data = func()
332 data = func()
318
333
319 # None signals to poll whether the process has exited
334 # None signals to poll whether the process has exited
320 if data is None:
335 if data is None:
321 #print("checking for process completion")
336 #print("checking for process completion")
322 if not GetExitCodeProcess(hprocess, ctypes.byref(exitCode)):
337 if not GetExitCodeProcess(hprocess, ctypes.byref(exitCode)):
323 raise ctypes.WinError()
338 raise ctypes.WinError()
324 if exitCode.value != STILL_ACTIVE:
339 if exitCode.value != STILL_ACTIVE:
325 return
340 return
326 # TESTING: Does zero-sized writefile help?
341 # TESTING: Does zero-sized writefile help?
327 if not WriteFile(handle, "", 0,
342 if not WriteFile(handle, "", 0,
328 ctypes.byref(bytesWritten), None):
343 ctypes.byref(bytesWritten), None):
329 raise ctypes.WinError()
344 raise ctypes.WinError()
330 continue
345 continue
331 #print("\nGot str %s\n" % repr(data), file=sys.stderr)
346 #print("\nGot str %s\n" % repr(data), file=sys.stderr)
332
347
333 # Encode the string to the console encoding
348 # Encode the string to the console encoding
334 if isinstance(data, unicode): #FIXME: Python3
349 if isinstance(data, unicode): #FIXME: Python3
335 data = data.encode('utf_8')
350 data = data.encode('utf_8')
336
351
337 # What we have now must be a string of bytes
352 # What we have now must be a string of bytes
338 if not isinstance(data, str): #FIXME: Python3
353 if not isinstance(data, str): #FIXME: Python3
339 raise RuntimeError("internal stdin function string error")
354 raise RuntimeError("internal stdin function string error")
340
355
341 # An empty string signals EOF
356 # An empty string signals EOF
342 if len(data) == 0:
357 if len(data) == 0:
343 return
358 return
344
359
345 # In a windows console, sometimes the input is echoed,
360 # In a windows console, sometimes the input is echoed,
346 # but sometimes not. How do we determine when to do this?
361 # but sometimes not. How do we determine when to do this?
347 stdout_func(data)
362 stdout_func(data)
348 # WriteFile may not accept all the data at once.
363 # WriteFile may not accept all the data at once.
349 # Loop until everything is processed
364 # Loop until everything is processed
350 while len(data) != 0:
365 while len(data) != 0:
351 #print("Calling writefile")
366 #print("Calling writefile")
352 if not WriteFile(handle, data, len(data),
367 if not WriteFile(handle, data, len(data),
353 ctypes.byref(bytesWritten), None):
368 ctypes.byref(bytesWritten), None):
369 # This occurs at exit
370 if GetLastError() == ERROR_NO_DATA:
371 return
354 raise ctypes.WinError()
372 raise ctypes.WinError()
355 #print("Called writefile")
373 #print("Called writefile")
356 data = data[bytesWritten.value:]
374 data = data[bytesWritten.value:]
357
375
358 def _stdout_thread(self, handle, func):
376 def _stdout_thread(self, handle, func):
359 # Allocate the output buffer
377 # Allocate the output buffer
360 data = ctypes.create_string_buffer(4096)
378 data = ctypes.create_string_buffer(4096)
361 while True:
379 while True:
362 bytesRead = DWORD(0)
380 bytesRead = DWORD(0)
363 if not ReadFile(handle, data, 4096,
381 if not ReadFile(handle, data, 4096,
364 ctypes.byref(bytesRead), None):
382 ctypes.byref(bytesRead), None):
365 le = GetLastError()
383 le = GetLastError()
366 if le == ERROR_BROKEN_PIPE:
384 if le == ERROR_BROKEN_PIPE:
367 return
385 return
368 else:
386 else:
369 raise ctypes.WinError()
387 raise ctypes.WinError()
370 # FIXME: Python3
388 # FIXME: Python3
371 s = data.value[0:bytesRead.value]
389 s = data.value[0:bytesRead.value]
372 #print("\nv: %s" % repr(s), file=sys.stderr)
390 #print("\nv: %s" % repr(s), file=sys.stderr)
373 func(s.decode('utf_8', 'replace'))
391 func(s.decode('utf_8', 'replace'))
374
392
375 def run(self, stdout_func = None, stdin_func = None, stderr_func = None):
393 def run(self, stdout_func = None, stdin_func = None, stderr_func = None):
376 """Runs the process, using the provided functions for I/O.
394 """Runs the process, using the provided functions for I/O.
377
395
378 The function stdin_func should return strings whenever a
396 The function stdin_func should return strings whenever a
379 character or characters become available.
397 character or characters become available.
380 The functions stdout_func and stderr_func are called whenever
398 The functions stdout_func and stderr_func are called whenever
381 something is printed to stdout or stderr, respectively.
399 something is printed to stdout or stderr, respectively.
382 These functions are called from different threads (but not
400 These functions are called from different threads (but not
383 concurrently, because of the GIL).
401 concurrently, because of the GIL).
384 """
402 """
385 if stdout_func == None and stdin_func == None and stderr_func == None:
403 if stdout_func == None and stdin_func == None and stderr_func == None:
386 return self._run_stdio()
404 return self._run_stdio()
387
405
388 if stderr_func != None and self.hstderr == None:
406 if stderr_func != None and self.mergeout:
389 raise RuntimeError("Shell command was initiated with "
407 raise RuntimeError("Shell command was initiated with "
390 "merged stdin/stdout, but a separate stderr_func "
408 "merged stdin/stdout, but a separate stderr_func "
391 "was provided to the run() method")
409 "was provided to the run() method")
392
410
393 # Create a thread for each input/output handle
411 # Create a thread for each input/output handle
412 stdin_thread = None
394 threads = []
413 threads = []
395 if stdin_func:
414 if stdin_func:
396 threads.append(threading.Thread(target=self._stdin_thread,
415 stdin_thread = threading.Thread(target=self._stdin_thread,
397 args=(self.hstdin, self.piProcInfo.hProcess,
416 args=(self.hstdin, self.piProcInfo.hProcess,
398 stdin_func, stdout_func)))
417 stdin_func, stdout_func))
399 threads.append(threading.Thread(target=self._stdout_thread,
418 threads.append(threading.Thread(target=self._stdout_thread,
400 args=(self.hstdout, stdout_func)))
419 args=(self.hstdout, stdout_func)))
401 if self.hstderr != None:
420 if not self.mergeout:
402 if stderr_func == None:
421 if stderr_func == None:
403 stderr_func = stdout_func
422 stderr_func = stdout_func
404 threads.append(threading.Thread(target=self._stdout_thread,
423 threads.append(threading.Thread(target=self._stdout_thread,
405 args=(self.hstderr, stderr_func)))
424 args=(self.hstderr, stderr_func)))
406 # Start the I/O threads and the process
425 # Start the I/O threads and the process
407 if ResumeThread(self.piProcInfo.hThread) == 0xFFFFFFFF:
426 if ResumeThread(self.piProcInfo.hThread) == 0xFFFFFFFF:
408 raise ctypes.WinError()
427 raise ctypes.WinError()
428 if stdin_thread is not None:
429 stdin_thread.start()
409 for thread in threads:
430 for thread in threads:
410 thread.start()
431 thread.start()
411 # Wait for the process to complete
432 # Wait for the process to complete
412 if WaitForSingleObject(self.piProcInfo.hProcess, INFINITE) == \
433 if WaitForSingleObject(self.piProcInfo.hProcess, INFINITE) == \
413 WAIT_FAILED:
434 WAIT_FAILED:
414 raise ctypes.WinError()
435 raise ctypes.WinError()
415 # Wait for the I/O threads to complete
436 # Wait for the I/O threads to complete
416 for thread in threads:
437 for thread in threads:
417 thread.join()
438 thread.join()
418
439
419 def _stdin_raw(self):
440 # Wait for the stdin thread to complete
420 """Uses msvcrt.kbhit/getwch to do read stdin without blocking"""
441 if stdin_thread is not None:
421 if msvcrt.kbhit():
442 stdin_thread.join()
422 #s = msvcrt.getwch()
443
423 s = msvcrt.getwch()
444 def _stdin_raw_nonblock(self):
424 # Key code for Enter is '\r', but need to give back '\n'
445 """Use the raw Win32 handle of sys.stdin to do non-blocking reads"""
425 if s == u'\r':
446 # WARNING: This is experimental, and produces inconsistent results.
426 s = u'\n'
447 # It's possible for the handle not to be appropriate for use
427 return s
448 # with WaitForSingleObject, among other things.
428 else:
449 handle = msvcrt.get_osfhandle(sys.stdin.fileno())
429 # This should make it poll at about 100 Hz, which
450 result = WaitForSingleObject(handle, 100)
430 # is hopefully good enough to be responsive but
451 if result == WAIT_FAILED:
431 # doesn't waste CPU.
452 raise ctypes.WinError()
432 time.sleep(0.01)
453 elif result == WAIT_TIMEOUT:
454 print(".", end='')
433 return None
455 return None
456 else:
457 data = ctypes.create_string_buffer(256)
458 bytesRead = DWORD(0)
459 print('?', end='')
460
461 if not ReadFile(handle, data, 256,
462 ctypes.byref(bytesRead), None):
463 raise ctypes.WinError()
464 # This ensures the non-blocking works with an actual console
465 # Not checking the error, so the processing will still work with
466 # other handle types
467 FlushConsoleInputBuffer(handle)
468
469 data = data.value
470 data = data.replace('\r\n', '\n')
471 data = data.replace('\r', '\n')
472 print(repr(data) + " ", end='')
473 return data
474
475 def _stdin_raw_block(self):
476 """Use a blocking stdin read"""
477 # The big problem with the blocking read is that it doesn't
478 # exit when it's supposed to in all contexts. An extra
479 # key-press may be required to trigger the exit.
480 try:
481 data = sys.stdin.read(1)
482 data = data.replace('\r', '\n')
483 return data
484 except WindowsError as we:
485 if we.winerror == ERROR_NO_DATA:
486 # This error occurs when the pipe is closed
487 return None
488 else:
489 # Otherwise let the error propagate
490 raise we
434
491
435 def _stdout_raw(self, s):
492 def _stdout_raw(self, s):
436 """Writes the string to stdout"""
493 """Writes the string to stdout"""
437 print(s, end='', file=sys.stdout)
494 print(s, end='', file=sys.stdout)
495 sys.stdout.flush()
438
496
439 def _stderr_raw(self, s):
497 def _stderr_raw(self, s):
440 """Writes the string to stdout"""
498 """Writes the string to stdout"""
441 print(s, end='', file=sys.stderr)
499 print(s, end='', file=sys.stderr)
500 sys.stderr.flush()
442
501
443 def _run_stdio(self):
502 def _run_stdio(self):
444 """Runs the process using the system standard I/O.
503 """Runs the process using the system standard I/O.
445
504
446 IMPORTANT: stdin needs to be asynchronous, so the Python
505 IMPORTANT: stdin needs to be asynchronous, so the Python
447 sys.stdin object is not used. Instead,
506 sys.stdin object is not used. Instead,
448 msvcrt.kbhit/getwch are used asynchronously.
507 msvcrt.kbhit/getwch are used asynchronously.
449 """
508 """
450 if self.hstderr != None:
509 # Disable Line and Echo mode
510 #lpMode = DWORD()
511 #handle = msvcrt.get_osfhandle(sys.stdin.fileno())
512 #if GetConsoleMode(handle, ctypes.byref(lpMode)):
513 # set_console_mode = True
514 # if not SetConsoleMode(handle, lpMode.value &
515 # ~(ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT)):
516 # raise ctypes.WinError()
517
518 if self.mergeout:
451 return self.run(stdout_func = self._stdout_raw,
519 return self.run(stdout_func = self._stdout_raw,
452 stdin_func = self._stdin_raw,
520 stdin_func = self._stdin_raw_block)
453 stderr_func = self._stderr_raw)
454 else:
521 else:
455 return self.run(stdout_func = self._stdout_raw,
522 return self.run(stdout_func = self._stdout_raw,
456 stdin_func = self._stdin_raw)
523 stdin_func = self._stdin_raw_block,
457
524 stderr_func = self._stderr_raw)
525
526 # Restore the previous console mode
527 #if set_console_mode:
528 # if not SetConsoleMode(handle, lpMode.value):
529 # raise ctypes.WinError()
458
530
459 def __exit__(self, exc_type, exc_value, traceback):
531 def __exit__(self, exc_type, exc_value, traceback):
460 if self.hstdin:
532 if self.hstdin:
461 CloseHandle(self.hstdin)
533 CloseHandle(self.hstdin)
462 self.hstdin = None
534 self.hstdin = None
463 if self.hstdout:
535 if self.hstdout:
464 CloseHandle(self.hstdout)
536 CloseHandle(self.hstdout)
465 self.hstdout = None
537 self.hstdout = None
466 if self.hstderr:
538 if self.hstderr:
467 CloseHandle(self.hstderr)
539 CloseHandle(self.hstderr)
468 self.hstderr = None
540 self.hstderr = None
469 if self.piProcInfo != None:
541 if self.piProcInfo != None:
470 CloseHandle(self.piProcInfo.hProcess)
542 CloseHandle(self.piProcInfo.hProcess)
471 CloseHandle(self.piProcInfo.hThread)
543 CloseHandle(self.piProcInfo.hThread)
472 self.piProcInfo = None
544 self.piProcInfo = None
473
545
474
546
475 def system(cmd):
547 def system(cmd):
476 """Win32 version of os.system() that works with network shares.
548 """Win32 version of os.system() that works with network shares.
477
549
478 Note that this implementation returns None, as meant for use in IPython.
550 Note that this implementation returns None, as meant for use in IPython.
479
551
480 Parameters
552 Parameters
481 ----------
553 ----------
482 cmd : str
554 cmd : str
483 A command to be executed in the system shell.
555 A command to be executed in the system shell.
484
556
485 Returns
557 Returns
486 -------
558 -------
487 None : we explicitly do NOT return the subprocess status code, as this
559 None : we explicitly do NOT return the subprocess status code, as this
488 utility is meant to be used extensively in IPython, where any return value
560 utility is meant to be used extensively in IPython, where any return value
489 would trigger :func:`sys.displayhook` calls.
561 would trigger :func:`sys.displayhook` calls.
490 """
562 """
491 with AvoidUNCPath() as path:
563 with AvoidUNCPath() as path:
492 if path is not None:
564 if path is not None:
493 cmd = '"pushd %s &&"%s' % (path, cmd)
565 cmd = '"pushd %s &&"%s' % (path, cmd)
494 with Win32ShellCommandController(cmd) as scc:
566 with Win32ShellCommandController(cmd) as scc:
495 scc.run()
567 scc.run()
496
568
497
569
498 if __name__ == "__main__":
570 if __name__ == "__main__":
499 print("Test starting!")
571 print("Test starting!")
500 #system("cmd")
572 #system("cmd")
501 system("python -i")
573 system("python -i")
502 print("Test finished!")
574 print("Test finished!")
General Comments 0
You need to be logged in to leave comments. Login now