##// END OF EJS Templates
Improve robustness, as suggested by Min.
Thomas Kluyver -
Show More
@@ -1,144 +1,144
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 58 else:
59 59 # We return None to signal that there was no change in the working
60 60 # directory
61 61 return None
62 62
63 63 def __exit__(self, exc_type, exc_value, traceback):
64 64 if self.is_unc_path:
65 65 os.chdir(self.path)
66 66
67 67
68 68 def _find_cmd(cmd):
69 69 """Find the full path to a .bat or .exe using the win32api module."""
70 70 try:
71 71 from win32api import SearchPath
72 72 except ImportError:
73 73 raise ImportError('you need to have pywin32 installed for this to work')
74 74 else:
75 75 PATH = os.environ['PATH']
76 76 extensions = ['.exe', '.com', '.bat', '.py']
77 77 path = None
78 78 for ext in extensions:
79 79 try:
80 80 path = SearchPath(PATH, cmd + ext)[0]
81 81 except:
82 82 pass
83 83 if path is None:
84 84 raise OSError("command %r not found" % cmd)
85 85 else:
86 86 return path
87 87
88 88
89 89 def _system_body(p):
90 90 """Callback for _system."""
91 enc = sys.stdin.encoding
91 enc = sys.stdin.encoding or sys.getdefaultencoding()
92 92 for line in read_no_interrupt(p.stdout).splitlines():
93 line = line.decode(enc)
93 line = line.decode(enc, 'replace')
94 94 print(line, file=sys.stdout)
95 95 for line in read_no_interrupt(p.stderr).splitlines():
96 line = line.decode(enc)
96 line = line.decode(enc, 'replace')
97 97 print(line, file=sys.stderr)
98 98
99 99
100 100 def system(cmd):
101 101 """Win32 version of os.system() that works with network shares.
102 102
103 103 Note that this implementation returns None, as meant for use in IPython.
104 104
105 105 Parameters
106 106 ----------
107 107 cmd : str
108 108 A command to be executed in the system shell.
109 109
110 110 Returns
111 111 -------
112 112 None : we explicitly do NOT return the subprocess status code, as this
113 113 utility is meant to be used extensively in IPython, where any return value
114 114 would trigger :func:`sys.displayhook` calls.
115 115 """
116 116 with AvoidUNCPath() as path:
117 117 if path is not None:
118 118 cmd = '"pushd %s &&"%s' % (path, cmd)
119 119 process_handler(cmd, _system_body)
120 120
121 121
122 122 def getoutput(cmd):
123 123 """Return standard output of executing cmd in a shell.
124 124
125 125 Accepts the same arguments as os.system().
126 126
127 127 Parameters
128 128 ----------
129 129 cmd : str
130 130 A command to be executed in the system shell.
131 131
132 132 Returns
133 133 -------
134 134 stdout : str
135 135 """
136 136
137 137 with AvoidUNCPath() as path:
138 138 if path is not None:
139 139 cmd = '"pushd %s &&"%s' % (path, cmd)
140 140 out = process_handler(cmd, lambda p: p.communicate()[0], STDOUT)
141 141
142 142 if out is None:
143 143 out = ''
144 144 return out
@@ -1,92 +1,96
1 1 import logging
2 2 import sys
3 3 import time
4 4 from cStringIO import StringIO
5 5
6 6 from session import extract_header, Message
7 7
8 8 from IPython.utils import io
9 9
10 10 #-----------------------------------------------------------------------------
11 11 # Globals
12 12 #-----------------------------------------------------------------------------
13 13
14 14 # Module-level logger
15 15 logger = logging.getLogger(__name__)
16 16
17 17 #-----------------------------------------------------------------------------
18 18 # Stream classes
19 19 #-----------------------------------------------------------------------------
20 20
21 21 class OutStream(object):
22 22 """A file like object that publishes the stream to a 0MQ PUB socket."""
23 23
24 24 # The time interval between automatic flushes, in seconds.
25 25 flush_interval = 0.05
26 26
27 27 def __init__(self, session, pub_socket, name):
28 28 self.session = session
29 29 self.pub_socket = pub_socket
30 30 self.name = name
31 31 self.parent_header = {}
32 32 self._new_buffer()
33 33
34 34 def set_parent(self, parent):
35 35 self.parent_header = extract_header(parent)
36 36
37 37 def close(self):
38 38 self.pub_socket = None
39 39
40 40 def flush(self):
41 41 #io.rprint('>>>flushing output buffer: %s<<<' % self.name) # dbg
42 42 if self.pub_socket is None:
43 43 raise ValueError(u'I/O operation on closed file')
44 44 else:
45 45 data = self._buffer.getvalue()
46 46 if data:
47 # Make sure that we're handling unicode
48 if not isinstance(data, unicode):
49 enc = sys.stdin.encoding or sys.getdefaultencoding()
50 data = data.decode(enc, 'replace')
47 51 content = {u'name':self.name, u'data':data}
48 52 msg = self.session.send(self.pub_socket, u'stream',
49 53 content=content,
50 54 parent=self.parent_header)
51 55 logger.debug(msg)
52 56 self._buffer.close()
53 57 self._new_buffer()
54 58
55 59 def isatty(self):
56 60 return False
57 61
58 62 def next(self):
59 63 raise IOError('Read not supported on a write only stream.')
60 64
61 65 def read(self, size=-1):
62 66 raise IOError('Read not supported on a write only stream.')
63 67
64 68 def readline(self, size=-1):
65 69 raise IOError('Read not supported on a write only stream.')
66 70
67 71 def write(self, string):
68 72 if self.pub_socket is None:
69 73 raise ValueError('I/O operation on closed file')
70 74 else:
71 75 # We can only send raw bytes, not unicode objects, so we encode
72 76 # into utf-8 for all frontends if we get unicode inputs.
73 77 if type(string) == unicode:
74 78 string = string.encode('utf-8')
75 79
76 80 self._buffer.write(string)
77 81 current_time = time.time()
78 82 if self._start <= 0:
79 83 self._start = current_time
80 84 elif current_time - self._start > self.flush_interval:
81 85 self.flush()
82 86
83 87 def writelines(self, sequence):
84 88 if self.pub_socket is None:
85 89 raise ValueError('I/O operation on closed file')
86 90 else:
87 91 for string in sequence:
88 92 self.write(string)
89 93
90 94 def _new_buffer(self):
91 95 self._buffer = StringIO()
92 96 self._start = -1
General Comments 0
You need to be logged in to leave comments. Login now