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