##// END OF EJS Templates
Merge branch 'unicode-win-process' of https://github.com/takluyver/ipython into takluyver-unicode-win-process
Thomas Kluyver -
r3535:ca434b70 merge
parent child Browse files
Show More
@@ -1,141 +1,144 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 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 or sys.getdefaultencoding()
91 92 for line in read_no_interrupt(p.stdout).splitlines():
93 line = line.decode(enc, 'replace')
92 94 print(line, file=sys.stdout)
93 95 for line in read_no_interrupt(p.stderr).splitlines():
96 line = line.decode(enc, 'replace')
94 97 print(line, file=sys.stderr)
95 98
96 99
97 100 def system(cmd):
98 101 """Win32 version of os.system() that works with network shares.
99 102
100 103 Note that this implementation returns None, as meant for use in IPython.
101 104
102 105 Parameters
103 106 ----------
104 107 cmd : str
105 108 A command to be executed in the system shell.
106 109
107 110 Returns
108 111 -------
109 112 None : we explicitly do NOT return the subprocess status code, as this
110 113 utility is meant to be used extensively in IPython, where any return value
111 114 would trigger :func:`sys.displayhook` calls.
112 115 """
113 116 with AvoidUNCPath() as path:
114 117 if path is not None:
115 118 cmd = '"pushd %s &&"%s' % (path, cmd)
116 119 process_handler(cmd, _system_body)
117 120
118 121
119 122 def getoutput(cmd):
120 123 """Return standard output of executing cmd in a shell.
121 124
122 125 Accepts the same arguments as os.system().
123 126
124 127 Parameters
125 128 ----------
126 129 cmd : str
127 130 A command to be executed in the system shell.
128 131
129 132 Returns
130 133 -------
131 134 stdout : str
132 135 """
133 136
134 137 with AvoidUNCPath() as path:
135 138 if path is not None:
136 139 cmd = '"pushd %s &&"%s' % (path, cmd)
137 140 out = process_handler(cmd, lambda p: p.communicate()[0], STDOUT)
138 141
139 142 if out is None:
140 143 out = ''
141 144 return out
@@ -1,92 +1,96 b''
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