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