##// END OF EJS Templates
add strict kwarg to win32 arg_split...
MinRK -
Show More
@@ -1,178 +1,183 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
23 23 from ctypes import c_int, POINTER
24 24 from ctypes.wintypes import LPCWSTR, HLOCAL
25 25 from subprocess import STDOUT
26 26
27 27 # our own imports
28 from ._process_common import read_no_interrupt, process_handler
28 from ._process_common import read_no_interrupt, process_handler, arg_split as py_arg_split
29 29 from . import py3compat
30 30 from . import text
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 Example
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.getcwdu()
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 = text.getdefaultencoding()
97 97 for line in read_no_interrupt(p.stdout).splitlines():
98 98 line = line.decode(enc, 'replace')
99 99 print(line, file=sys.stdout)
100 100 for line in read_no_interrupt(p.stderr).splitlines():
101 101 line = line.decode(enc, 'replace')
102 102 print(line, file=sys.stderr)
103 103
104 104 # Wait to finish for returncode
105 105 return p.wait()
106 106
107 107
108 108 def system(cmd):
109 109 """Win32 version of os.system() that works with network shares.
110 110
111 111 Note that this implementation returns None, as meant for use in IPython.
112 112
113 113 Parameters
114 114 ----------
115 115 cmd : str
116 116 A command to be executed in the system shell.
117 117
118 118 Returns
119 119 -------
120 120 None : we explicitly do NOT return the subprocess status code, as this
121 121 utility is meant to be used extensively in IPython, where any return value
122 122 would trigger :func:`sys.displayhook` calls.
123 123 """
124 124 with AvoidUNCPath() as path:
125 125 if path is not None:
126 126 cmd = '"pushd %s &&"%s' % (path, cmd)
127 127 return process_handler(cmd, _system_body)
128 128
129 129
130 130 def getoutput(cmd):
131 131 """Return standard output of executing cmd in a shell.
132 132
133 133 Accepts the same arguments as os.system().
134 134
135 135 Parameters
136 136 ----------
137 137 cmd : str
138 138 A command to be executed in the system shell.
139 139
140 140 Returns
141 141 -------
142 142 stdout : str
143 143 """
144 144
145 145 with AvoidUNCPath() as path:
146 146 if path is not None:
147 147 cmd = '"pushd %s &&"%s' % (path, cmd)
148 148 out = process_handler(cmd, lambda p: p.communicate()[0], STDOUT)
149 149
150 150 if out is None:
151 151 out = ''
152 152 return out
153 153
154 154 try:
155 155 CommandLineToArgvW = ctypes.windll.shell32.CommandLineToArgvW
156 156 CommandLineToArgvW.arg_types = [LPCWSTR, POINTER(c_int)]
157 157 CommandLineToArgvW.res_types = [POINTER(LPCWSTR)]
158 158 LocalFree = ctypes.windll.kernel32.LocalFree
159 159 LocalFree.res_type = HLOCAL
160 160 LocalFree.arg_types = [HLOCAL]
161 161
162 def arg_split(commandline, posix=False):
162 def arg_split(commandline, posix=False, strict=True):
163 163 """Split a command line's arguments in a shell-like manner.
164 164
165 165 This is a special version for windows that use a ctypes call to CommandLineToArgvW
166 166 to do the argv splitting. The posix paramter is ignored.
167
168 If strict=False, process_common.arg_split(...strict=False) is used instead.
167 169 """
168 170 #CommandLineToArgvW returns path to executable if called with empty string.
169 171 if commandline.strip() == "":
170 172 return []
173 if not strict:
174 # not really a cl-arg, fallback on _process_common
175 return py_arg_split(commandline, posix=posix, strict=strict)
171 176 argvn = c_int()
172 177 result_pointer = CommandLineToArgvW(py3compat.cast_unicode(commandline.lstrip()), ctypes.byref(argvn))
173 178 result_array_type = LPCWSTR * argvn.value
174 179 result = [arg for arg in result_array_type.from_address(result_pointer)]
175 180 retval = LocalFree(result_pointer)
176 181 return result
177 182 except AttributeError:
178 from ._process_common import arg_split
183 arg_split = py_arg_split
General Comments 0
You need to be logged in to leave comments. Login now