##// END OF EJS Templates
Match pre-existing reality.
Itamar Turner-Trauring -
Show More
@@ -1,201 +1,199 b''
1 1 """Windows-specific implementation of process utilities.
2 2
3 3 This file is only meant to be imported by process.py, not by end-users.
4 4 """
5 5
6 6 #-----------------------------------------------------------------------------
7 7 # Copyright (C) 2010-2011 The IPython Development Team
8 8 #
9 9 # Distributed under the terms of the BSD License. The full license is in
10 10 # the file COPYING, distributed as part of this software.
11 11 #-----------------------------------------------------------------------------
12 12
13 13 #-----------------------------------------------------------------------------
14 14 # Imports
15 15 #-----------------------------------------------------------------------------
16 16
17 17 # stdlib
18 18 import os
19 19 import sys
20 20 import ctypes
21 21 import time
22 22
23 23 from ctypes import c_int, POINTER
24 24 from ctypes.wintypes import LPCWSTR, HLOCAL
25 25 from subprocess import STDOUT, TimeoutExpired
26 26
27 27 # our own imports
28 28 from ._process_common import read_no_interrupt, process_handler, arg_split as py_arg_split
29 29 from . import py3compat
30 30 from .encoding import DEFAULT_ENCODING
31 31
32 32 #-----------------------------------------------------------------------------
33 33 # Function definitions
34 34 #-----------------------------------------------------------------------------
35 35
36 36 class AvoidUNCPath(object):
37 37 """A context manager to protect command execution from UNC paths.
38 38
39 39 In the Win32 API, commands can't be invoked with the cwd being a UNC path.
40 40 This context manager temporarily changes directory to the 'C:' drive on
41 41 entering, and restores the original working directory on exit.
42 42
43 43 The context manager returns the starting working directory *if* it made a
44 44 change and None otherwise, so that users can apply the necessary adjustment
45 45 to their system calls in the event of a change.
46 46
47 47 Examples
48 48 --------
49 49 ::
50 50 cmd = 'dir'
51 51 with AvoidUNCPath() as path:
52 52 if path is not None:
53 53 cmd = '"pushd %s &&"%s' % (path, cmd)
54 54 os.system(cmd)
55 55 """
56 56 def __enter__(self):
57 57 self.path = os.getcwd()
58 58 self.is_unc_path = self.path.startswith(r"\\")
59 59 if self.is_unc_path:
60 60 # change to c drive (as cmd.exe cannot handle UNC addresses)
61 61 os.chdir("C:")
62 62 return self.path
63 63 else:
64 64 # We return None to signal that there was no change in the working
65 65 # directory
66 66 return None
67 67
68 68 def __exit__(self, exc_type, exc_value, traceback):
69 69 if self.is_unc_path:
70 70 os.chdir(self.path)
71 71
72 72
73 73 def _find_cmd(cmd):
74 74 """Find the full path to a .bat or .exe using the win32api module."""
75 75 try:
76 76 from win32api import SearchPath
77 77 except ImportError:
78 78 raise ImportError('you need to have pywin32 installed for this to work')
79 79 else:
80 80 PATH = os.environ['PATH']
81 81 extensions = ['.exe', '.com', '.bat', '.py']
82 82 path = None
83 83 for ext in extensions:
84 84 try:
85 85 path = SearchPath(PATH, cmd, ext)[0]
86 86 except:
87 87 pass
88 88 if path is None:
89 89 raise OSError("command %r not found" % cmd)
90 90 else:
91 91 return path
92 92
93 93
94 94 def _system_body(p):
95 95 """Callback for _system."""
96 96 enc = DEFAULT_ENCODING
97 97 print("READING...")
98 98 for line in read_no_interrupt(p.stdout).splitlines():
99 99 line = line.decode(enc, 'replace')
100 100 print(line, file=sys.stdout)
101 101 for line in read_no_interrupt(p.stderr).splitlines():
102 102 line = line.decode(enc, 'replace')
103 103 print(line, file=sys.stderr)
104 104
105 105 # Wait to finish for returncode. Unfortunately, Python has a bug where
106 106 # wait() isn't interruptible (https://bugs.python.org/issue28168) so poll in
107 107 # a loop instead of just doing `return p.wait()`.
108 108 while True:
109 109 result = p.poll()
110 110 print("POLLED")
111 111 if result is None:
112 112 time.sleep(0.01)
113 113 else:
114 114 return result
115 115
116 116
117 117 def system(cmd):
118 118 """Win32 version of os.system() that works with network shares.
119 119
120 120 Note that this implementation returns None, as meant for use in IPython.
121 121
122 122 Parameters
123 123 ----------
124 124 cmd : str or list
125 125 A command to be executed in the system shell.
126 126
127 127 Returns
128 128 -------
129 None : we explicitly do NOT return the subprocess status code, as this
130 utility is meant to be used extensively in IPython, where any return value
131 would trigger :func:`sys.displayhook` calls.
129 int : child process' exit code.
132 130 """
133 131 # The controller provides interactivity with both
134 132 # stdin and stdout
135 133 #import _process_win32_controller
136 134 #_process_win32_controller.system(cmd)
137 135
138 136 with AvoidUNCPath() as path:
139 137 if path is not None:
140 138 cmd = '"pushd %s &&"%s' % (path, cmd)
141 139 return process_handler(cmd, _system_body)
142 140
143 141 def getoutput(cmd):
144 142 """Return standard output of executing cmd in a shell.
145 143
146 144 Accepts the same arguments as os.system().
147 145
148 146 Parameters
149 147 ----------
150 148 cmd : str or list
151 149 A command to be executed in the system shell.
152 150
153 151 Returns
154 152 -------
155 153 stdout : str
156 154 """
157 155
158 156 with AvoidUNCPath() as path:
159 157 if path is not None:
160 158 cmd = '"pushd %s &&"%s' % (path, cmd)
161 159 out = process_handler(cmd, lambda p: p.communicate()[0], STDOUT)
162 160
163 161 if out is None:
164 162 out = b''
165 163 return py3compat.decode(out)
166 164
167 165 try:
168 166 CommandLineToArgvW = ctypes.windll.shell32.CommandLineToArgvW
169 167 CommandLineToArgvW.arg_types = [LPCWSTR, POINTER(c_int)]
170 168 CommandLineToArgvW.restype = POINTER(LPCWSTR)
171 169 LocalFree = ctypes.windll.kernel32.LocalFree
172 170 LocalFree.res_type = HLOCAL
173 171 LocalFree.arg_types = [HLOCAL]
174 172
175 173 def arg_split(commandline, posix=False, strict=True):
176 174 """Split a command line's arguments in a shell-like manner.
177 175
178 176 This is a special version for windows that use a ctypes call to CommandLineToArgvW
179 177 to do the argv splitting. The posix parameter is ignored.
180 178
181 179 If strict=False, process_common.arg_split(...strict=False) is used instead.
182 180 """
183 181 #CommandLineToArgvW returns path to executable if called with empty string.
184 182 if commandline.strip() == "":
185 183 return []
186 184 if not strict:
187 185 # not really a cl-arg, fallback on _process_common
188 186 return py_arg_split(commandline, posix=posix, strict=strict)
189 187 argvn = c_int()
190 188 result_pointer = CommandLineToArgvW(py3compat.cast_unicode(commandline.lstrip()), ctypes.byref(argvn))
191 189 result_array_type = LPCWSTR * argvn.value
192 190 result = [arg for arg in result_array_type.from_address(ctypes.addressof(result_pointer.contents))]
193 191 retval = LocalFree(result_pointer)
194 192 return result
195 193 except AttributeError:
196 194 arg_split = py_arg_split
197 195
198 196 def check_pid(pid):
199 197 # OpenProcess returns 0 if no such process (of ours) exists
200 198 # positive int otherwise
201 199 return bool(ctypes.windll.kernel32.OpenProcess(1,0,pid))
General Comments 0
You need to be logged in to leave comments. Login now