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