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