##// END OF EJS Templates
Fix small bug in UNC path context manager.
Fernando Perez -
Show More
@@ -1,137 +1,141 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 The IPython Development Team
7 # Copyright (C) 2010 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
21
22 from subprocess import STDOUT
22 from subprocess import STDOUT
23
23
24 # our own imports
24 # our own imports
25 from ._process_common import read_no_interrupt, process_handler
25 from ._process_common import read_no_interrupt, process_handler
26
26
27 #-----------------------------------------------------------------------------
27 #-----------------------------------------------------------------------------
28 # Function definitions
28 # Function definitions
29 #-----------------------------------------------------------------------------
29 #-----------------------------------------------------------------------------
30
30
31 class AvoidUNCPath(object):
31 class AvoidUNCPath(object):
32 """A context manager to protect command execution from UNC paths.
32 """A context manager to protect command execution from UNC paths.
33
33
34 In the Win32 API, commands can't be invoked with the cwd being a UNC path.
34 In the Win32 API, commands can't be invoked with the cwd being a UNC path.
35 This context manager temporarily changes directory to the 'C:' drive on
35 This context manager temporarily changes directory to the 'C:' drive on
36 entering, and restores the original working directory on exit.
36 entering, and restores the original working directory on exit.
37
37
38 The context manager returns the starting working directory *if* it made a
38 The context manager returns the starting working directory *if* it made a
39 change and None otherwise, so that users can apply the necessary adjustment
39 change and None otherwise, so that users can apply the necessary adjustment
40 to their system calls in the event of a change.
40 to their system calls in the event of a change.
41
41
42 Example
42 Example
43 -------
43 -------
44 ::
44 ::
45 cmd = 'dir'
45 cmd = 'dir'
46 with AvoidUNCPath() as path:
46 with AvoidUNCPath() as path:
47 if path is not None:
47 if path is not None:
48 cmd = '"pushd %s &&"%s' % (path, cmd)
48 cmd = '"pushd %s &&"%s' % (path, cmd)
49 os.system(cmd)
49 os.system(cmd)
50 """
50 """
51 def __enter__(self):
51 def __enter__(self):
52 self.path = os.getcwd()
52 self.path = os.getcwd()
53 self.is_unc_path = self.path.startswith(r"\\")
53 self.is_unc_path = self.path.startswith(r"\\")
54 if self.is_unc_path:
54 if self.is_unc_path:
55 # change to c drive (as cmd.exe cannot handle UNC addresses)
55 # change to c drive (as cmd.exe cannot handle UNC addresses)
56 os.chdir("C:")
56 os.chdir("C:")
57 return self.path
57 return self.path
58 else:
59 # We return None to signal that there was no change in the working
60 # directory
61 return None
58
62
59 def __exit__(self, exc_type, exc_value, traceback):
63 def __exit__(self, exc_type, exc_value, traceback):
60 if self.is_unc_path:
64 if self.is_unc_path:
61 os.chdir(self.path)
65 os.chdir(self.path)
62
66
63
67
64 def _find_cmd(cmd):
68 def _find_cmd(cmd):
65 """Find the full path to a .bat or .exe using the win32api module."""
69 """Find the full path to a .bat or .exe using the win32api module."""
66 try:
70 try:
67 from win32api import SearchPath
71 from win32api import SearchPath
68 except ImportError:
72 except ImportError:
69 raise ImportError('you need to have pywin32 installed for this to work')
73 raise ImportError('you need to have pywin32 installed for this to work')
70 else:
74 else:
71 PATH = os.environ['PATH']
75 PATH = os.environ['PATH']
72 extensions = ['.exe', '.com', '.bat', '.py']
76 extensions = ['.exe', '.com', '.bat', '.py']
73 path = None
77 path = None
74 for ext in extensions:
78 for ext in extensions:
75 try:
79 try:
76 path = SearchPath(PATH, cmd + ext)[0]
80 path = SearchPath(PATH, cmd + ext)[0]
77 except:
81 except:
78 pass
82 pass
79 if path is None:
83 if path is None:
80 raise OSError("command %r not found" % cmd)
84 raise OSError("command %r not found" % cmd)
81 else:
85 else:
82 return path
86 return path
83
87
84
88
85 def _system_body(p):
89 def _system_body(p):
86 """Callback for _system."""
90 """Callback for _system."""
87 for line in read_no_interrupt(p.stdout).splitlines():
91 for line in read_no_interrupt(p.stdout).splitlines():
88 print(line, file=sys.stdout)
92 print(line, file=sys.stdout)
89 for line in read_no_interrupt(p.stderr).splitlines():
93 for line in read_no_interrupt(p.stderr).splitlines():
90 print(line, file=sys.stderr)
94 print(line, file=sys.stderr)
91
95
92
96
93 def system(cmd):
97 def system(cmd):
94 """Win32 version of os.system() that works with network shares.
98 """Win32 version of os.system() that works with network shares.
95
99
96 Note that this implementation returns None, as meant for use in IPython.
100 Note that this implementation returns None, as meant for use in IPython.
97
101
98 Parameters
102 Parameters
99 ----------
103 ----------
100 cmd : str
104 cmd : str
101 A command to be executed in the system shell.
105 A command to be executed in the system shell.
102
106
103 Returns
107 Returns
104 -------
108 -------
105 None : we explicitly do NOT return the subprocess status code, as this
109 None : we explicitly do NOT return the subprocess status code, as this
106 utility is meant to be used extensively in IPython, where any return value
110 utility is meant to be used extensively in IPython, where any return value
107 would trigger :func:`sys.displayhook` calls.
111 would trigger :func:`sys.displayhook` calls.
108 """
112 """
109 with AvoidUNCPath() as path:
113 with AvoidUNCPath() as path:
110 if path is not None:
114 if path is not None:
111 cmd = '"pushd %s &&"%s' % (path, cmd)
115 cmd = '"pushd %s &&"%s' % (path, cmd)
112 process_handler(cmd, _system_body)
116 process_handler(cmd, _system_body)
113
117
114
118
115 def getoutput(cmd):
119 def getoutput(cmd):
116 """Return standard output of executing cmd in a shell.
120 """Return standard output of executing cmd in a shell.
117
121
118 Accepts the same arguments as os.system().
122 Accepts the same arguments as os.system().
119
123
120 Parameters
124 Parameters
121 ----------
125 ----------
122 cmd : str
126 cmd : str
123 A command to be executed in the system shell.
127 A command to be executed in the system shell.
124
128
125 Returns
129 Returns
126 -------
130 -------
127 stdout : str
131 stdout : str
128 """
132 """
129
133
130 with AvoidUNCPath() as path:
134 with AvoidUNCPath() as path:
131 if path is not None:
135 if path is not None:
132 cmd = '"pushd %s &&"%s' % (path, cmd)
136 cmd = '"pushd %s &&"%s' % (path, cmd)
133 out = process_handler(cmd, lambda p: p.communicate()[0], STDOUT)
137 out = process_handler(cmd, lambda p: p.communicate()[0], STDOUT)
134
138
135 if out is None:
139 if out is None:
136 out = ''
140 out = ''
137 return out
141 return out
General Comments 0
You need to be logged in to leave comments. Login now