##// END OF EJS Templates
Remove utils.autoattr
Thomas Kluyver -
Show More
@@ -1,197 +1,197 b''
1 1 """Posix-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-2011 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 subprocess as sp
20 20 import sys
21 21
22 22 from IPython.external import pexpect
23 23
24 24 # Our own
25 from .autoattr import auto_attr
26 25 from ._process_common import getoutput, arg_split
27 26 from IPython.utils import py3compat
28 27 from IPython.utils.encoding import DEFAULT_ENCODING
29 28
30 29 #-----------------------------------------------------------------------------
31 30 # Function definitions
32 31 #-----------------------------------------------------------------------------
33 32
34 33 def _find_cmd(cmd):
35 34 """Find the full path to a command using which."""
36 35
37 36 path = sp.Popen(['/usr/bin/env', 'which', cmd],
38 37 stdout=sp.PIPE, stderr=sp.PIPE).communicate()[0]
39 38 return py3compat.bytes_to_str(path)
40 39
41 40
42 41 class ProcessHandler(object):
43 42 """Execute subprocesses under the control of pexpect.
44 43 """
45 44 # Timeout in seconds to wait on each reading of the subprocess' output.
46 45 # This should not be set too low to avoid cpu overusage from our side,
47 46 # since we read in a loop whose period is controlled by this timeout.
48 47 read_timeout = 0.05
49 48
50 49 # Timeout to give a process if we receive SIGINT, between sending the
51 50 # SIGINT to the process and forcefully terminating it.
52 51 terminate_timeout = 0.2
53 52
54 53 # File object where stdout and stderr of the subprocess will be written
55 54 logfile = None
56 55
57 56 # Shell to call for subprocesses to execute
58 sh = None
57 _sh = None
59 58
60 @auto_attr
61 59 def sh(self):
62 sh = pexpect.which('sh')
63 if sh is None:
64 raise OSError('"sh" shell not found')
65 return sh
60 if self._sh is None:
61 self._sh = pexpect.which('sh')
62 if self._sh is None:
63 raise OSError('"sh" shell not found')
64
65 return self._sh
66 66
67 67 def __init__(self, logfile=None, read_timeout=None, terminate_timeout=None):
68 68 """Arguments are used for pexpect calls."""
69 69 self.read_timeout = (ProcessHandler.read_timeout if read_timeout is
70 70 None else read_timeout)
71 71 self.terminate_timeout = (ProcessHandler.terminate_timeout if
72 72 terminate_timeout is None else
73 73 terminate_timeout)
74 74 self.logfile = sys.stdout if logfile is None else logfile
75 75
76 76 def getoutput(self, cmd):
77 77 """Run a command and return its stdout/stderr as a string.
78 78
79 79 Parameters
80 80 ----------
81 81 cmd : str
82 82 A command to be executed in the system shell.
83 83
84 84 Returns
85 85 -------
86 86 output : str
87 87 A string containing the combination of stdout and stderr from the
88 88 subprocess, in whatever order the subprocess originally wrote to its
89 89 file descriptors (so the order of the information in this string is the
90 90 correct order as would be seen if running the command in a terminal).
91 91 """
92 92 try:
93 93 return pexpect.run(self.sh, args=['-c', cmd]).replace('\r\n', '\n')
94 94 except KeyboardInterrupt:
95 95 print('^C', file=sys.stderr, end='')
96 96
97 97 def getoutput_pexpect(self, cmd):
98 98 """Run a command and return its stdout/stderr as a string.
99 99
100 100 Parameters
101 101 ----------
102 102 cmd : str
103 103 A command to be executed in the system shell.
104 104
105 105 Returns
106 106 -------
107 107 output : str
108 108 A string containing the combination of stdout and stderr from the
109 109 subprocess, in whatever order the subprocess originally wrote to its
110 110 file descriptors (so the order of the information in this string is the
111 111 correct order as would be seen if running the command in a terminal).
112 112 """
113 113 try:
114 114 return pexpect.run(self.sh, args=['-c', cmd]).replace('\r\n', '\n')
115 115 except KeyboardInterrupt:
116 116 print('^C', file=sys.stderr, end='')
117 117
118 118 def system(self, cmd):
119 119 """Execute a command in a subshell.
120 120
121 121 Parameters
122 122 ----------
123 123 cmd : str
124 124 A command to be executed in the system shell.
125 125
126 126 Returns
127 127 -------
128 128 int : child's exitstatus
129 129 """
130 130 # Get likely encoding for the output.
131 131 enc = DEFAULT_ENCODING
132 132
133 133 # Patterns to match on the output, for pexpect. We read input and
134 134 # allow either a short timeout or EOF
135 135 patterns = [pexpect.TIMEOUT, pexpect.EOF]
136 136 # the index of the EOF pattern in the list.
137 137 # even though we know it's 1, this call means we don't have to worry if
138 138 # we change the above list, and forget to change this value:
139 139 EOF_index = patterns.index(pexpect.EOF)
140 140 # The size of the output stored so far in the process output buffer.
141 141 # Since pexpect only appends to this buffer, each time we print we
142 142 # record how far we've printed, so that next time we only print *new*
143 143 # content from the buffer.
144 144 out_size = 0
145 145 try:
146 146 # Since we're not really searching the buffer for text patterns, we
147 147 # can set pexpect's search window to be tiny and it won't matter.
148 148 # We only search for the 'patterns' timeout or EOF, which aren't in
149 149 # the text itself.
150 150 #child = pexpect.spawn(pcmd, searchwindowsize=1)
151 151 if hasattr(pexpect, 'spawnb'):
152 152 child = pexpect.spawnb(self.sh, args=['-c', cmd]) # Pexpect-U
153 153 else:
154 154 child = pexpect.spawn(self.sh, args=['-c', cmd]) # Vanilla Pexpect
155 155 flush = sys.stdout.flush
156 156 while True:
157 157 # res is the index of the pattern that caused the match, so we
158 158 # know whether we've finished (if we matched EOF) or not
159 159 res_idx = child.expect_list(patterns, self.read_timeout)
160 160 print(child.before[out_size:].decode(enc, 'replace'), end='')
161 161 flush()
162 162 if res_idx==EOF_index:
163 163 break
164 164 # Update the pointer to what we've already printed
165 165 out_size = len(child.before)
166 166 except KeyboardInterrupt:
167 167 # We need to send ^C to the process. The ascii code for '^C' is 3
168 168 # (the character is known as ETX for 'End of Text', see
169 169 # curses.ascii.ETX).
170 170 child.sendline(chr(3))
171 171 # Read and print any more output the program might produce on its
172 172 # way out.
173 173 try:
174 174 out_size = len(child.before)
175 175 child.expect_list(patterns, self.terminate_timeout)
176 176 print(child.before[out_size:].decode(enc, 'replace'), end='')
177 177 sys.stdout.flush()
178 178 except KeyboardInterrupt:
179 179 # Impatient users tend to type it multiple times
180 180 pass
181 181 finally:
182 182 # Ensure the subprocess really is terminated
183 183 child.terminate(force=True)
184 184 # add isalive check, to ensure exitstatus is set:
185 185 child.isalive()
186 186 return child.exitstatus
187 187
188 188
189 189 # Make system() with a functional interface for outside use. Note that we use
190 190 # getoutput() from the _common utils, which is built on top of popen(). Using
191 191 # pexpect to get subprocess output produces difficult to parse output, since
192 192 # programs think they are talking to a tty and produce highly formatted output
193 193 # (ls is a good example) that makes them hard.
194 194 system = ProcessHandler().system
195 195
196 196
197 197
1 NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now